<commit_before>/*
 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
 * Copyright (C) 2007 Apple Inc. All rights reserved.
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "core/svg/SVGLengthContext.h"

#include "core/css/CSSHelper.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/layout/LayoutObject.h"
#include "core/style/ComputedStyle.h"
#include "core/svg/SVGSVGElement.h"
#include "platform/LengthFunctions.h"
#include "platform/fonts/FontMetrics.h"

namespace blink {

static inline float dimensionForLengthMode(SVGLengthMode mode, const FloatSize& viewportSize)
{
    switch (mode) {
    case SVGLengthMode::Width:
        return viewportSize.width();
    case SVGLengthMode::Height:
        return viewportSize.height();
    case SVGLengthMode::Other:
        return sqrtf(viewportSize.diagonalLengthSquared() / 2);
    }
    ASSERT_NOT_REACHED();
    return 0;
}

static float convertValueFromPercentageToUserUnits(const SVGLength& value, const FloatSize& viewportSize)
{
    return CSSPrimitiveValue::clampToCSSLengthRange(value.scaleByPercentage(dimensionForLengthMode(value.unitMode(), viewportSize)));
}

static const ComputedStyle* computedStyleForLengthResolving(const SVGElement* context)
{
    if (!context)
        return nullptr;

    const ContainerNode* currentContext = context;
    do {
        if (currentContext->layoutObject())
            return currentContext->layoutObject()->style();
        currentContext = currentContext->parentNode();
    } while (currentContext);

    // There must be at least a LayoutSVGRoot layoutObject, carrying a style.
    ASSERT_NOT_REACHED();
    return nullptr;
}

static const ComputedStyle* rootElementStyle(const Node* context)
{
    if (!context)
        return nullptr;

    const Document& document = context->document();
    Node* documentElement = document.documentElement();
    const ComputedStyle* documentStyle = document.computedStyle();
    const ComputedStyle* style = documentElement && context != documentElement ? documentElement->computedStyle() : documentStyle;
    if (!style)
        style = documentStyle;
    return style;
}

static float convertValueFromUserUnitsToEMS(const ComputedStyle* style, float value)
{
    if (!style)
        return 0;
    float fontSize = style->specifiedFontSize();
    if (!fontSize)
        return 0;
    return value / fontSize;
}

static float convertValueFromEMSToUserUnits(const ComputedStyle* style, float value)
{
    if (!style)
        return 0;
    return value * style->specifiedFontSize();
}

SVGLengthContext::SVGLengthContext(const SVGElement* context)
    : m_context(context)
{
}

FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height)
{
    ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
    if (type != SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE && !viewport.isEmpty()) {
        const FloatSize& viewportSize = viewport.size();
        return FloatRect(
            convertValueFromPercentageToUserUnits(x, viewportSize) + viewport.x(),
            convertValueFromPercentageToUserUnits(y, viewportSize) + viewport.y(),
            convertValueFromPercentageToUserUnits(width, viewportSize),
            convertValueFromPercentageToUserUnits(height, viewportSize));
    }

    SVGLengthContext lengthContext(context);
    return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext));
}

FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x, const SVGLength& y)
{
    ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
    if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
        SVGLengthContext lengthContext(context);
        return FloatPoint(x.value(lengthContext), y.value(lengthContext));
    }

    // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
    return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage());
}

float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x)
{
    ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
    if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
        SVGLengthContext lengthContext(context);
        return x.value(lengthContext);
    }

    // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
    return x.valueAsPercentage();
}

float SVGLengthContext::valueForLength(const UnzoomedLength& unzoomedLength, SVGLengthMode mode) const
{
    return valueForLength(unzoomedLength.length(), 1, mode);
}

float SVGLengthContext::valueForLength(const Length& length, const ComputedStyle& style, SVGLengthMode mode) const
{
    return valueForLength(length, style.effectiveZoom(), mode);
}

float SVGLengthContext::valueForLength(const Length& length, float zoom, SVGLengthMode mode) const
{
    float dimension = 0;
    if (length.hasPercent()) {
        FloatSize viewportSize;
        determineViewport(viewportSize);
        // The viewport will be unaffected by zoom.
        dimension = dimensionForLengthMode(mode, viewportSize);
    }
    return valueForLength(length, zoom, dimension);
}

float SVGLengthContext::valueForLength(const Length& length, const ComputedStyle& style, float dimension)
{
    return valueForLength(length, style.effectiveZoom(), dimension);
}

float SVGLengthContext::valueForLength(const Length& length, float zoom, float dimension)
{
    ASSERT(zoom != 0);
    // isIntrinsic can occur for 'width' and 'height', but has no
    // real meaning for svg.
    if (length.isIntrinsic() || length.isLegacyIntrinsic())
        return 0;
    return floatValueForLength(length, dimension * zoom) / zoom;
}

float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit) const
{
    float userUnits = value;
    switch (fromUnit) {
    case LengthTypeUnknown:
        return 0;
    case LengthTypePX:
    case LengthTypeNumber:
        userUnits = value;
        break;
    case LengthTypePercentage: {
        FloatSize viewportSize;
        if (!determineViewport(viewportSize))
            return 0;
        userUnits = value * dimensionForLengthMode(mode, viewportSize) / 100;
        break;
    }
    case LengthTypeEMS:
        userUnits = convertValueFromEMSToUserUnits(computedStyleForLengthResolving(m_context), value);
        break;
    case LengthTypeEXS:
        userUnits = convertValueFromEXSToUserUnits(value);
        break;
    case LengthTypeCM:
        userUnits = value * cssPixelsPerCentimeter;
        break;
    case LengthTypeMM:
        userUnits = value * cssPixelsPerMillimeter;
        break;
    case LengthTypeIN:
        userUnits = value * cssPixelsPerInch;
        break;
    case LengthTypePT:
        userUnits = value * cssPixelsPerPoint;
        break;
    case LengthTypePC:
        userUnits = value * cssPixelsPerPica;
        break;
    case LengthTypeREMS:
        userUnits = convertValueFromEMSToUserUnits(rootElementStyle(m_context), value);
        break;
    case LengthTypeCHS:
        userUnits = convertValueFromCHSToUserUnits(value);
        break;
    default:
        ASSERT_NOT_REACHED();
        break;
    }

    // Since we mix css <length> values with svg's length values we need to
    // clamp values to the narrowest range, otherwise it can result in
    // rendering issues.
    return CSSPrimitiveValue::clampToCSSLengthRange(userUnits);
}

float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit) const
{
    switch (toUnit) {
    case LengthTypeUnknown:
        return 0;
    case LengthTypeNumber:
        return value;
    case LengthTypePercentage: {
        FloatSize viewportSize;
        if (!determineViewport(viewportSize))
            return 0;
        // LengthTypePercentage is represented with 100% = 100.0.
        // Good for accuracy but could eventually be changed.
        return value * 100 / dimensionForLengthMode(mode, viewportSize);
    }
    case LengthTypeEMS:
        return convertValueFromUserUnitsToEMS(computedStyleForLengthResolving(m_context), value);
    case LengthTypeEXS:
        return convertValueFromUserUnitsToEXS(value);
    case LengthTypeREMS:
        return convertValueFromUserUnitsToEMS(rootElementStyle(m_context), value);
    case LengthTypeCHS:
        return convertValueFromUserUnitsToCHS(value);
    case LengthTypePX:
        return value;
    case LengthTypeCM:
        return value / cssPixelsPerCentimeter;
    case LengthTypeMM:
        return value / cssPixelsPerMillimeter;
    case LengthTypeIN:
        return value / cssPixelsPerInch;
    case LengthTypePT:
        return value / cssPixelsPerPoint;
    case LengthTypePC:
        return value / cssPixelsPerPica;
    }

    ASSERT_NOT_REACHED();
    return 0;
}

float SVGLengthContext::convertValueFromUserUnitsToCHS(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    float zeroWidth = style->fontMetrics().zeroWidth();
    if (!zeroWidth)
        return 0;

    return value / zeroWidth;
}

float SVGLengthContext::convertValueFromCHSToUserUnits(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    return value * style->fontMetrics().zeroWidth();
}

float SVGLengthContext::convertValueFromUserUnitsToEXS(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
    // if this causes problems in real world cases maybe it would be best to remove this
    float xHeight = ceilf(style->fontMetrics().xHeight());
    if (!xHeight)
        return 0;

    return value / xHeight;
}

float SVGLengthContext::convertValueFromEXSToUserUnits(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
    // if this causes problems in real world cases maybe it would be best to remove this
    return value * ceilf(style->fontMetrics().xHeight());
}

bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const
{
    if (!m_context)
        return false;

    // Root <svg> element lengths are resolved against the top level viewport.
    if (m_context->isOutermostSVGSVGElement()) {
        viewportSize = toSVGSVGElement(m_context)->currentViewportSize();
        return true;
    }

    // Take size from nearest viewport element.
    SVGElement* viewportElement = m_context->viewportElement();
    if (!isSVGSVGElement(viewportElement))
        return false;

    const SVGSVGElement& svg = toSVGSVGElement(*viewportElement);
    viewportSize = svg.currentViewBoxRect().size();
    if (viewportSize.isEmpty())
        viewportSize = svg.currentViewportSize();

    return true;
}

}
<commit_msg>Avoid division-by-zero in SVGLengthContext::convertValueFromUserUnits<commit_after>/*
 * Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
 * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
 * Copyright (C) 2007 Apple Inc. All rights reserved.
 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "core/svg/SVGLengthContext.h"

#include "core/css/CSSHelper.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/dom/NodeComputedStyle.h"
#include "core/layout/LayoutObject.h"
#include "core/style/ComputedStyle.h"
#include "core/svg/SVGSVGElement.h"
#include "platform/LengthFunctions.h"
#include "platform/fonts/FontMetrics.h"

namespace blink {

static inline float dimensionForLengthMode(SVGLengthMode mode, const FloatSize& viewportSize)
{
    switch (mode) {
    case SVGLengthMode::Width:
        return viewportSize.width();
    case SVGLengthMode::Height:
        return viewportSize.height();
    case SVGLengthMode::Other:
        return sqrtf(viewportSize.diagonalLengthSquared() / 2);
    }
    ASSERT_NOT_REACHED();
    return 0;
}

static float convertValueFromPercentageToUserUnits(const SVGLength& value, const FloatSize& viewportSize)
{
    return CSSPrimitiveValue::clampToCSSLengthRange(value.scaleByPercentage(dimensionForLengthMode(value.unitMode(), viewportSize)));
}

static const ComputedStyle* computedStyleForLengthResolving(const SVGElement* context)
{
    if (!context)
        return nullptr;

    const ContainerNode* currentContext = context;
    do {
        if (currentContext->layoutObject())
            return currentContext->layoutObject()->style();
        currentContext = currentContext->parentNode();
    } while (currentContext);

    // There must be at least a LayoutSVGRoot layoutObject, carrying a style.
    ASSERT_NOT_REACHED();
    return nullptr;
}

static const ComputedStyle* rootElementStyle(const Node* context)
{
    if (!context)
        return nullptr;

    const Document& document = context->document();
    Node* documentElement = document.documentElement();
    const ComputedStyle* documentStyle = document.computedStyle();
    const ComputedStyle* style = documentElement && context != documentElement ? documentElement->computedStyle() : documentStyle;
    if (!style)
        style = documentStyle;
    return style;
}

static float convertValueFromUserUnitsToEMS(const ComputedStyle* style, float value)
{
    if (!style)
        return 0;
    float fontSize = style->specifiedFontSize();
    if (!fontSize)
        return 0;
    return value / fontSize;
}

static float convertValueFromEMSToUserUnits(const ComputedStyle* style, float value)
{
    if (!style)
        return 0;
    return value * style->specifiedFontSize();
}

SVGLengthContext::SVGLengthContext(const SVGElement* context)
    : m_context(context)
{
}

FloatRect SVGLengthContext::resolveRectangle(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const FloatRect& viewport, const SVGLength& x, const SVGLength& y, const SVGLength& width, const SVGLength& height)
{
    ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
    if (type != SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE && !viewport.isEmpty()) {
        const FloatSize& viewportSize = viewport.size();
        return FloatRect(
            convertValueFromPercentageToUserUnits(x, viewportSize) + viewport.x(),
            convertValueFromPercentageToUserUnits(y, viewportSize) + viewport.y(),
            convertValueFromPercentageToUserUnits(width, viewportSize),
            convertValueFromPercentageToUserUnits(height, viewportSize));
    }

    SVGLengthContext lengthContext(context);
    return FloatRect(x.value(lengthContext), y.value(lengthContext), width.value(lengthContext), height.value(lengthContext));
}

FloatPoint SVGLengthContext::resolvePoint(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x, const SVGLength& y)
{
    ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
    if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
        SVGLengthContext lengthContext(context);
        return FloatPoint(x.value(lengthContext), y.value(lengthContext));
    }

    // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
    return FloatPoint(x.valueAsPercentage(), y.valueAsPercentage());
}

float SVGLengthContext::resolveLength(const SVGElement* context, SVGUnitTypes::SVGUnitType type, const SVGLength& x)
{
    ASSERT(type != SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN);
    if (type == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
        SVGLengthContext lengthContext(context);
        return x.value(lengthContext);
    }

    // FIXME: valueAsPercentage() won't be correct for eg. cm units. They need to be resolved in user space and then be considered in objectBoundingBox space.
    return x.valueAsPercentage();
}

float SVGLengthContext::valueForLength(const UnzoomedLength& unzoomedLength, SVGLengthMode mode) const
{
    return valueForLength(unzoomedLength.length(), 1, mode);
}

float SVGLengthContext::valueForLength(const Length& length, const ComputedStyle& style, SVGLengthMode mode) const
{
    return valueForLength(length, style.effectiveZoom(), mode);
}

float SVGLengthContext::valueForLength(const Length& length, float zoom, SVGLengthMode mode) const
{
    float dimension = 0;
    if (length.hasPercent()) {
        FloatSize viewportSize;
        determineViewport(viewportSize);
        // The viewport will be unaffected by zoom.
        dimension = dimensionForLengthMode(mode, viewportSize);
    }
    return valueForLength(length, zoom, dimension);
}

float SVGLengthContext::valueForLength(const Length& length, const ComputedStyle& style, float dimension)
{
    return valueForLength(length, style.effectiveZoom(), dimension);
}

float SVGLengthContext::valueForLength(const Length& length, float zoom, float dimension)
{
    ASSERT(zoom != 0);
    // isIntrinsic can occur for 'width' and 'height', but has no
    // real meaning for svg.
    if (length.isIntrinsic() || length.isLegacyIntrinsic())
        return 0;
    return floatValueForLength(length, dimension * zoom) / zoom;
}

float SVGLengthContext::convertValueToUserUnits(float value, SVGLengthMode mode, SVGLengthType fromUnit) const
{
    float userUnits = value;
    switch (fromUnit) {
    case LengthTypeUnknown:
        return 0;
    case LengthTypePX:
    case LengthTypeNumber:
        userUnits = value;
        break;
    case LengthTypePercentage: {
        FloatSize viewportSize;
        if (!determineViewport(viewportSize))
            return 0;
        userUnits = value * dimensionForLengthMode(mode, viewportSize) / 100;
        break;
    }
    case LengthTypeEMS:
        userUnits = convertValueFromEMSToUserUnits(computedStyleForLengthResolving(m_context), value);
        break;
    case LengthTypeEXS:
        userUnits = convertValueFromEXSToUserUnits(value);
        break;
    case LengthTypeCM:
        userUnits = value * cssPixelsPerCentimeter;
        break;
    case LengthTypeMM:
        userUnits = value * cssPixelsPerMillimeter;
        break;
    case LengthTypeIN:
        userUnits = value * cssPixelsPerInch;
        break;
    case LengthTypePT:
        userUnits = value * cssPixelsPerPoint;
        break;
    case LengthTypePC:
        userUnits = value * cssPixelsPerPica;
        break;
    case LengthTypeREMS:
        userUnits = convertValueFromEMSToUserUnits(rootElementStyle(m_context), value);
        break;
    case LengthTypeCHS:
        userUnits = convertValueFromCHSToUserUnits(value);
        break;
    default:
        ASSERT_NOT_REACHED();
        break;
    }

    // Since we mix css <length> values with svg's length values we need to
    // clamp values to the narrowest range, otherwise it can result in
    // rendering issues.
    return CSSPrimitiveValue::clampToCSSLengthRange(userUnits);
}

float SVGLengthContext::convertValueFromUserUnits(float value, SVGLengthMode mode, SVGLengthType toUnit) const
{
    switch (toUnit) {
    case LengthTypeUnknown:
        return 0;
    case LengthTypeNumber:
        return value;
    case LengthTypePercentage: {
        FloatSize viewportSize;
        if (!determineViewport(viewportSize))
            return 0;
        float dimension = dimensionForLengthMode(mode, viewportSize);
        if (!dimension)
            return 0;
        // LengthTypePercentage is represented with 100% = 100.0.
        // Good for accuracy but could eventually be changed.
        return value * 100 / dimension;
    }
    case LengthTypeEMS:
        return convertValueFromUserUnitsToEMS(computedStyleForLengthResolving(m_context), value);
    case LengthTypeEXS:
        return convertValueFromUserUnitsToEXS(value);
    case LengthTypeREMS:
        return convertValueFromUserUnitsToEMS(rootElementStyle(m_context), value);
    case LengthTypeCHS:
        return convertValueFromUserUnitsToCHS(value);
    case LengthTypePX:
        return value;
    case LengthTypeCM:
        return value / cssPixelsPerCentimeter;
    case LengthTypeMM:
        return value / cssPixelsPerMillimeter;
    case LengthTypeIN:
        return value / cssPixelsPerInch;
    case LengthTypePT:
        return value / cssPixelsPerPoint;
    case LengthTypePC:
        return value / cssPixelsPerPica;
    }

    ASSERT_NOT_REACHED();
    return 0;
}

float SVGLengthContext::convertValueFromUserUnitsToCHS(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    float zeroWidth = style->fontMetrics().zeroWidth();
    if (!zeroWidth)
        return 0;

    return value / zeroWidth;
}

float SVGLengthContext::convertValueFromCHSToUserUnits(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    return value * style->fontMetrics().zeroWidth();
}

float SVGLengthContext::convertValueFromUserUnitsToEXS(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
    // if this causes problems in real world cases maybe it would be best to remove this
    float xHeight = ceilf(style->fontMetrics().xHeight());
    if (!xHeight)
        return 0;

    return value / xHeight;
}

float SVGLengthContext::convertValueFromEXSToUserUnits(float value) const
{
    const ComputedStyle* style = computedStyleForLengthResolving(m_context);
    if (!style)
        return 0;

    // Use of ceil allows a pixel match to the W3Cs expected output of coords-units-03-b.svg
    // if this causes problems in real world cases maybe it would be best to remove this
    return value * ceilf(style->fontMetrics().xHeight());
}

bool SVGLengthContext::determineViewport(FloatSize& viewportSize) const
{
    if (!m_context)
        return false;

    // Root <svg> element lengths are resolved against the top level viewport.
    if (m_context->isOutermostSVGSVGElement()) {
        viewportSize = toSVGSVGElement(m_context)->currentViewportSize();
        return true;
    }

    // Take size from nearest viewport element.
    SVGElement* viewportElement = m_context->viewportElement();
    if (!isSVGSVGElement(viewportElement))
        return false;

    const SVGSVGElement& svg = toSVGSVGElement(*viewportElement);
    viewportSize = svg.currentViewBoxRect().size();
    if (viewportSize.isEmpty())
        viewportSize = svg.currentViewportSize();

    return true;
}

}
<|endoftext|>


<commit_before>#include <ncurses.h>
#include <time.h>
#include <stdio.h>

#include <string>
#include <algorithm>

#include "global/Global.h"  // includes difficulties
#include "world/World.h"
#include "world/Map.h"

#include "game.h"
#include "menu.h"
#include "entities/Player.h"
#include "entities/Enemy.h"
#include "entities/Coin.h"
#include "windows/Window.h"
#include "windows/DiagWindow.h"

using namespace std;

int startGame() {

    /*
     * Pre-game loop init.
     * TODO
     *  1) Init entities.
     *  2) Init map with entities.
     *  3) draw map.
     */

    // Retrieve global refs
    Global *global = Global::get();
    int numCoins   = global->getNoCoins();
    int numEnemies = global->getNoEnemies();

    // Declared internally, to prevent ctors from running before main
    DiagWindow diagWin_game = DiagWindow({{41, 1}, {100,10}});
    Window wgame = Window(game_area);
    Map map = Map(&wgame);

    // Actors know about game window for movement
    Player player = Player();

    // TODO Maybe combine with below...
    std::vector<Enemy> enemies;
    for (int i = 0; i < numEnemies; i++)
        enemies.push_back(Enemy());
    std::vector<Coin> coins;
    for (int i = 0; i < numCoins; i++)
        coins.push_back(Coin());

    /*
     * Check if Player & Enemy are too near or too far
     * from each other.  Somewhere between a quarter
     * of a screen and half of a screen.
     */
    //std::vector<Enemy*> enemies;
        //enemies.push_back(new Enemy());
    uint_fast8_t quarterArea = (game_area.area() / 4);
    uint_fast8_t halfArea = (game_area.area() / 2);
    uint_fast8_t distance;
    bool dangerClose = true;
    while (dangerClose == true) {
        for (Enemy &enemy : enemies) {
            enemy = Enemy(player);
            distance = player.getDistance(enemy.getPos());
            if (distance < quarterArea || distance > halfArea) {
                dangerClose = true;
                break;
            } else {
                //enemies.push_back(enemy);
                dangerClose = false;
            }
        }
    }

    /* Init placement of Player, Enemy, and Coins
     * Map prioritises drawing items in the reverse
     * of the order that they were added
     */
    map.push(player);
    for (Coin  &coin  : coins)   map.push(coin);
    for (Enemy &enemy : enemies) map.push(enemy);

    /*
     * Game loop begin.
     */

    int ch, coinsCollected = 0;
    string infoMsg = "";
    bool isGameover = false;
    while (isGameover == false) {
        // Advance record of world time
        global->tick();

        // Draw all entities
        map.draw();

        ch      = wgame.getChar();
        infoMsg = "";
        vec2ui prevPos = player.getPos();

        switch (ch) {
            /*
             * Diagonal keys
             */
            case 55:  // Key up-left
                player.moveNorthWest();
                break;
            case 57:  // Key up-right
                player.moveNorthEast();
                break;
            case 51:  // Key down-right
                player.moveSouthEast();
                break;
            case 49:  // Key down-left
                player.moveSouthWest();
                break;

            /*
             * Orthogonal keys
             */
            case KEY_UP:
            case 56:
            case 'k':
                player.moveNorth();
                break;
            case KEY_DOWN:
            case 50:
            case 'j':
                player.moveSouth();
                break;
            case KEY_LEFT:
            case 52:
            case 'h':
                player.moveWest();
                break;
            case KEY_RIGHT:
            case 54:
            case 'l':
                player.moveEast();
                break;
            case KEY_ENTER: /* numpad enter */
            case '\n':      /* keyboard return */
                break;
            case 27:        /* ESC */
                isGameover = true;
            default:
                player.wait();
                infoMsg = player.getName();

        }
        if (map.checkCell(player.getPos(), "Enemy")) {
            //player.attack(&map.getEntity(player.getPos()));
            for (Enemy &enemy : enemies) {
                if (enemy.getPos() == player.getPos()) {
                    player.attack(enemy);
                    //printf("%ld\n\r", enemy.getHP());
                }
            }
            player.setPos(prevPos);
        }

        // Draw Coins again, and check if player has landed on
        for (auto &coin : coins) {
            // Don't do anything unless coin 'belongs' to world
            if (coin.getOwnership() == WORLD) {  // XXX without this, player still get invisicoins
                if (player.atop(coin.getPos())) {
                    coin.setOwnership(PLAYER);
                    player.addItem(coin);
                    map.rm(coin);

                    if ( ++coinsCollected == numCoins) {
                        isGameover = true;
                    }
                }
            }
        }

        // Enemy, seek out player
        string proximityAlert = "";
        for (size_t i = 0; i < enemies.size(); i++) {
            if (enemies[i].getHP() > 0) {  // TODO some sort of alive or dead flag
                enemies[i].move();
                if (enemies[i].isAdjacent(player.getPos())) {
                    proximityAlert += "!";
                }
            } else { // pop dead enemy
                enemies.erase(enemies.begin() + i);
            }
        }

        for (Enemy &enemy : enemies) {
            // Cleanup dead enemies
            if (enemy.getHP() <= 0) {
                //printf("Enemy %s is dead!\n", enemy.getType().c_str());
                map.rm(enemy);
            }
            // Game Over
            if (enemy.atop(player.getPos())) {
                wgame.coloSplash(COLOR_PAIR(1));

                diagWin_game.push("GAME OVER!");
                isGameover = true;
                break;
            }
        }

        diagWin_game.push(
            + "HP: "
            + std::to_string(player.getHP())
            + " DEF: "
            + std::to_string(player.getDEF())
            + " ATK: "
            + std::to_string(player.getATK())
            + " ACT: "
            + std::to_string(player.getACT())
            + " LCK: "
            + std::to_string(player.getLCK())
            + " stp: "
            + std::to_string(player.getSteps())
            + " ptk: "
            + std::to_string(player.getTicks())
            + " gtk: "
            + std::to_string(global->getTicks())
            + " scr: "
            + std::to_string(player.getScore())
            + " dif: " + global->getDifficultyStr()
            + " nfo: " + infoMsg
            + " " + proximityAlert
        );
    }

    /*
     * Post-game loop operations.
     */
    map.draw();  // Draw last frame.

    // TODO eventually return more information
    return player.getScore();
}

<commit_msg>[#71] Implement Actor v Actor fighting<commit_after>#include <ncurses.h>
#include <time.h>
#include <stdio.h>

#include <string>
#include <algorithm>

#include "global/Global.h"  // includes difficulties
#include "world/World.h"
#include "world/Map.h"

#include "game.h"
#include "menu.h"
#include "entities/Player.h"
#include "entities/Enemy.h"
#include "entities/Coin.h"
#include "windows/Window.h"
#include "windows/DiagWindow.h"

using namespace std;

int startGame() {

    /*
     * Pre-game loop init.
     * TODO
     *  1) Init entities.
     *  2) Init map with entities.
     *  3) draw map.
     */

    // Retrieve global refs
    Global *global = Global::get();
    int numCoins   = global->getNoCoins();
    int numEnemies = global->getNoEnemies();

    // Declared internally, to prevent ctors from running before main
    DiagWindow diagWin_game = DiagWindow({{41, 1}, {100,10}});
    Window wgame = Window(game_area);
    Map map = Map(&wgame);

    // Actors know about game window for movement
    Player player = Player();

    // TODO Maybe combine with below...
    std::vector<Enemy> enemies;
    for (int i = 0; i < numEnemies; i++)
        enemies.push_back(Enemy());
    std::vector<Coin> coins;
    for (int i = 0; i < numCoins; i++)
        coins.push_back(Coin());

    /*
     * Check if Player & Enemy are too near or too far
     * from each other.  Somewhere between a quarter
     * of a screen and half of a screen.
     */
    //std::vector<Enemy*> enemies;
        //enemies.push_back(new Enemy());
    uint_fast8_t quarterArea = (game_area.area() / 4);
    uint_fast8_t halfArea = (game_area.area() / 2);
    uint_fast8_t distance;
    bool dangerClose = true;
    while (dangerClose == true) {
        for (Enemy &enemy : enemies) {
            enemy = Enemy(player);
            distance = player.getDistance(enemy.getPos());
            if (distance < quarterArea || distance > halfArea) {
                dangerClose = true;
                break;
            } else {
                //enemies.push_back(enemy);
                dangerClose = false;
            }
        }
    }

    /* Init placement of Player, Enemy, and Coins
     * Map prioritises drawing items in the reverse
     * of the order that they were added
     */
    map.push(player);
    for (Coin  &coin  : coins)   map.push(coin);
    for (Enemy &enemy : enemies) map.push(enemy);

    /*
     * Game loop begin.
     */

    int ch, coinsCollected = 0;
    string infoMsg = "";
    bool isGameover = false;
    while (isGameover == false) {
        // Advance record of world time
        global->tick();

        // Draw all entities
        map.draw();

        ch      = wgame.getChar();
        infoMsg = "";
        vec2ui prevPos = player.getPos();

        switch (ch) {
            /*
             * Diagonal keys
             */
            case 55:  // Key up-left
                player.moveNorthWest();
                break;
            case 57:  // Key up-right
                player.moveNorthEast();
                break;
            case 51:  // Key down-right
                player.moveSouthEast();
                break;
            case 49:  // Key down-left
                player.moveSouthWest();
                break;

            /*
             * Orthogonal keys
             */
            case KEY_UP:
            case 56:
            case 'k':
                player.moveNorth();
                break;
            case KEY_DOWN:
            case 50:
            case 'j':
                player.moveSouth();
                break;
            case KEY_LEFT:
            case 52:
            case 'h':
                player.moveWest();
                break;
            case KEY_RIGHT:
            case 54:
            case 'l':
                player.moveEast();
                break;
            case KEY_ENTER: /* numpad enter */
            case '\n':      /* keyboard return */
                break;
            case 27:        /* ESC */
                isGameover = true;
            default:
                player.wait();
                infoMsg = player.getName();

        }
        if (map.checkCell(player.getPos(), "Enemy")) {
            for (Enemy &enemy : enemies) {
                if (enemy.getPos() == player.getPos()) {
                    player.attack(enemy);
                }
            }
            player.setPos(prevPos);
        }

        // Draw Coins again, and check if player has landed on
        // TODO replace this for..range with std iter for loop
        for (auto &coin : coins) {
            if (player.atop(coin.getPos())) {
                coin.setOwnership(PLAYER);
                player.addItem(coin);

                // Erase this coin if owned by Player
                coins.erase(
                    std::remove_if(
                        coins.begin(),
                        coins.end(),
                        [](Coin& c) -> bool {
                            return c.getOwnership() == PLAYER;
                        }
                    ),
                    coins.end()
                );

                if ( ++coinsCollected == numCoins) {
                    isGameover = true;
                }
            }
        }

        // Enemy, seek out player
        string proximityAlert = "";
        for (size_t i = 0; i < enemies.size(); i++) {
            if (enemies[i].getHP() > 0) {  // TODO some sort of alive or dead flag
                enemies[i].move();
                if (enemies[i].isAdjacent(player.getPos())) {
                    proximityAlert += "!";
                }
            } else { // pop dead enemy
                diagWin_game.push(player.getName()
                        + " killed " + enemies[i].getType()
                        + " " + enemies[i].getName() + "!");
                enemies.erase(enemies.begin() + i);
            }
        }

        for (Enemy &enemy : enemies) {
            // Game Over
            if (enemy.atop(player.getPos())) {
                wgame.coloSplash(COLOR_PAIR(1));

                diagWin_game.push("GAME OVER!");
                isGameover = true;
                break;
            }
        }

        diagWin_game.push(
            + "HP: "
            + std::to_string(player.getHP())
            + " DEF: "
            + std::to_string(player.getDEF())
            + " ATK: "
            + std::to_string(player.getATK())
            + " ACT: "
            + std::to_string(player.getACT())
            + " LCK: "
            + std::to_string(player.getLCK())
            + " stp: "
            + std::to_string(player.getSteps())
            + " ptk: "
            + std::to_string(player.getTicks())
            + " gtk: "
            + std::to_string(global->getTicks())
            + " scr: "
            + std::to_string(player.getScore())
            + " dif: " + global->getDifficultyStr()
            + " nfo: " + infoMsg
            + " " + proximityAlert
        );

        // Re-init map with refs to updated entities
        map = Map(&wgame);
        map.push(player);
        for (Coin  &coin  : coins)   map.push(coin);
        for (Enemy &enemy : enemies) map.push(enemy);
    }

    /*
     * Post-game loop operations.
     */
    map.draw();  // Draw last frame.

    // TODO eventually return more information
    return player.getScore();
}

<|endoftext|>


<commit_before>/**
 * @file    game.h
 * @brief   Arquivo cabeçalho com a implementacao de funcoes
            que controlam o jogo.
 * @author  Daniel Barbosa (nome@email.com)
 * @author  Jaine Budke (jainebudke@hotmail.com)
 * @since   30/05/2017
 * @date    19/06/2017
 */

#include "game.h"
#include "snake.h"

Level lv; // instanciando classe level
Snake sk; // instanciando classe snake

// ======================================================
// ACTIONS
// ======================================================

/** @brief Identifica a posicao inicial da Snake no tabuleiro.
    @return Posicao inicial da Snake */
Position Game::initialPosition(){

    // Percorre board tentando encontrar inicio
    for( int i = 0 ; i < lv.currentBoard.size() ; i++ ){        // Percorre cada pos do vector
        for( int j=0 ; j < lv.currentBoard[i].length() ; j++ ){ // Percorre cada pos da string
            if( lv.currentBoard[i][j] == '*' ){
                Position inicial;
                inicial.x = j; // coluna
            	   inicial.y = i; // linha
                lv.initial = inicial;
            	   return inicial;
            }
        }
    }

}

/** @brief Lança uma maçã no jogo, dentro das coordenadas do tabuleiro da fase.
    @return Posicao em que a maçã foi lançada */
Position Game::throwApple(){

    bool invalido = true;

    std::srand(std::time(0)); // semente do rand
    Position tamanho = sizesBoards[lv.currentLevel-1]; // pega tamanho do level atual

    // sorteia a maçã até cair num espaço livre
    do{

        int menor = 1;
        int maiorLinha  = tamanho.y -1;
        int maiorColuna = tamanho.x -1;

        Position pos;

        pos.y = rand()%(maiorLinha-menor+1) + menor;  // sorteia um numero aleatorio para a linha
        pos.x = rand()%(maiorColuna-menor+1) + menor; // sorteia um numero aleatorio para a coluna

        if( isFree(lv.currentBoard[pos.y][pos.x]) ){
            invalido = false;
            lv.currentBoard[pos.y][pos.x] = 'o';
            maca = pos;
        }

    } while( invalido );

}

/** @brief Move a cobra de acordo com as coordenadas passadas.
     @return 1 se cobra chegou na maca, bateu na parede ou rabo. 0 otherwise. */
bool Game::moveSnake(){

    Position dir = sk.listDirections[sk.currentDirection];
    sk.snake.push_front( dir );

    Position back = sk.snake.back();

    sk.snake.pop_back();

    Position front = sk.snake.front();

    lv.currentBoard[back.y][back.x] = ' ';
    lv.currentBoard[front.y][front.x] = '~';

    // se colidiu de alguma forma
    if( collideTail() or collideWall() ){
        currentState = CRASH;
        return true; // muda stop pra true
    }

    // se chegou na maçã
    if( eatingApple() ){
        currentState = EXPAND;
        lv.eatenApples += 1;
        sk.listDirections.clear();

        return true; // muda stop pra true
    }

    if( sk.listDirections.size() == sk.currentDirection ){
        return true;
    }

    sk.currentDirection += 1;

    return false; // muda stop pra false - continua rodando

}

// ======================================================
// AUXILIO
// ======================================================

/** @brief Verifica se o caractere passado é uma parede.
     @return 1 se for, 0 se não for */
bool Game::isWall( char ch ){
    return ( ch == '#' );
}

/** @brief Verifica se o caractere passado é uma parede invisivel.
     @return 1 se for, 0 se não for */
bool Game::isInvisibleWall( char ch ){
    return ( ch == '.' );
}

/** @brief Verifica se o caractere passado é um lugar livre.
     @return 1 se for, 0 se não for */
bool Game::isFree( char ch ){
    return ( ch == ' ' );
}

/** @brief Verifica se o caractere passado é a posicao inicial.
     @return 1 se for, 0 se não for */
bool Game::isInitialPosition( char ch ){
    return ( ch == '*' );
}

/** @brief Verifica se a snake colidiu com o tail.
     @return 1 se colidiu, 0 otherwise */
bool Game::collideTail( ){

    // TODO

    return false;
}

/** @brief Verifica se a snake colidiu com a parede.
     @return 1 se colidiu, 0 otherwise */
bool Game::collideWall( ){

    // TODO

    return false;
}

/** @brief Verifica se a snake chegou na maca.
     @return 1 se chegou, 0 otherwise */
bool Game::eatingApple( ){

    Position pos = sk.snake.front();

    if( pos.x == maca.x and pos.y == maca.y ){
        return true;
    } else {
        return false;
    }

}

// ======================================================
// ESTADOS
// ======================================================

/** @brief Aumenta o tamanho da cobra. */
void Game::expandSnake(){

    Position pos = initialPosition();

    throwApple();

    // SE tamanho da snake for 1 ela é transformada na cobra
    if( sk.sizeSnake == 0 ){
        lv.currentBoard[pos.y][pos.x] = '~';
        sk.snake.push_front( pos );

        sk.sizeSnake += 1; // snake cresce
    }

    // SE tamanho da snake for maior que 1 ela cresce
    if( sk.sizeSnake >= 1 ){
        sk.sizeSnake += 1; // snake cresce
    }

    // SE qntidade de maçãs comidas for o total -> STATE = LEVEL_UP
    if( lv.eatenApples == lv.totalApples ){
        currentState = LEVEL_UP;
    }

    // SE qntidade de maçãs comidas for menor que o total -> STATE = RUN e lança maçã
    if( lv.eatenApples < lv.totalApples ){
        currentState = RUN;
    }

}

/** @brief Faz a chamada da próxima fase do jogo setando os valores da classe Level. */
void Game::levelUp(){

    lv.currentLevel += 1; // em level é acrescentado um nível

    if( lv.currentLevel <= levels ){
        lv.currentBoard = boards[lv.currentLevel-1]; // recupera o tabuleiro do level
    }

    currentState = EXPAND;

}

/** @brief Verifica se a cobra teve alguma colisão. */
bool Game::crashSnake(){

    std::cout << "Oh no! You're crash!\n";
    currentState = DEAD;

}

/** @brief Simula a morte da cobra (diminui uma vida). */
void Game::deadSnake(){

    lives -= 1;

    std::cout << ">>> Pressione <ENTER> quando estiver pronto para continuar.";
    std::string dummy;
    std::getline( std::cin, dummy );

    currentState = RUN;

}

/** @brief A cobra anda a quantidade de vezes até chegar na maçã. */
void Game::runSnake(){

    sk.solveMaze( lv.currentBoard, lv.initial, sizesBoards[ lv.currentLevel - 1 ], maca );

    bool stop = moveSnake();

    // verifica se a condicao de parada foi acionada
    if( stop == false ){
        currentState = RUN;
    }

}

// ======================================================
// SETTERS AND GETTERS
// ======================================================

/** @brief Define a quantidade de fases do jogo.
    @param levels_ As fases
    @return True se tiver um numero de fases maior que zero; False otherwise. */
bool Game::setLevels( int levels_ ){
	if( levels_ > 0 ){
		levels = levels_;
		return true;
	} else {
		return false;
	}
}

/** @brief Recupera a quantidade de fases do jogo.
    @return As fases. */
int Game::getLevels( void ) const{
	return levels;
}

/** @brief Define um vetor com os tabuleiros do jogo.
    @param boards_ Os tabuleiros
    @return True se tiver um numero de tabuleiros maior que zero; False otherwise. */
bool Game::setBoards( std::vector<std::vector<std::string>> boards_ ){
	int qntidade = boards_.size();
	if( qntidade > 0 ){
		boards = boards_;
		return true;
	} else {
		return false;
	}
}

/** @brief Recupera os tabuleiros do jogo.
    @return Os tabuleiros. */
std::vector<std::vector<std::string>> Game::getBoards( void ) const{
	return boards;
}

/** @brief Diminui uma vida do jogador.
    @return True se tinha vidas pra diminuir; False otherwise. */
bool Game::setLives( ){
	if( lives > 0 ){
		lives--;
		return true;
	} else {
		return false;
	}
}

/** @brief Recupera a quantidade de vidas do jogador.
    @return As vidas. */
int Game::getLives( void ) const{
	return lives;
}

/** @brief Atualiza status do jogador
    @param state_ O status */
void Game::setState( int state_ ){
	state = state_;
}

/** @brief Recupera o status do jogador
    @return True se jogador ganhou; False se perdeu */
bool Game::getState( void ) const{
	if( state == 1 ){
		return true;
	}else if( state == 0 ){
		return false;
	}
}

/** @brief Atualiza os tamanhos dos tabuleiros do jogo
    @param sizesBoards_ Vetor com os tamanhos */
void Game::setSizeBoards( std::vector<Position> sizesBoards_ ){
	sizesBoards = sizesBoards_;
}

/** @brief Recupera o vetor com os tamanhos */
std::vector<Position> Game::getSizeBoards( void ) const{
	return sizesBoards;
}

// ======================================================
// GETTERS AND SETTERS DA CLASSE LEVEL
// ======================================================

/** @brief Define a fase atual que o jogador se encontra.
    @param level_ A fase
    @return True se tiver um numero de fases maior que zero; False otherwise. */
bool Game::setCurrentLevel( int level_ ){
    if( level_ > 0 ){
        lv.currentLevel = level_;
        return true;
    } else {
        return false;
    }
}

/** @brief Recupera a fase atual do jogo.
    @return A fase. */
int Game::getCurrentLevel( void ) const{
    return lv.currentLevel;
}

/** @brief Recupera o tabuleiro atual do jogo.
    @return O tabuleiro. */
std::vector<std::string> Game::getCurrentBoard( void ) const{
    return lv.currentBoard;
}

/** @brief Define o tabuleiro atual do jogo.
    @param level_ O tabuleiro
    @return True se tiver um tabuleiro; False otherwise. */
bool setCurrentBoard( std::vector<std::string> tabuleiro ){
    lv.currentBoard = tabuleiro;
}

/** @brief Recupera as macas comidas do jogo.
     @return A quandiade de macas. */
int Game::getEatenApples( void ) {
    return lv.eatenApples;
}<commit_msg>pegando macas corretamente<commit_after>/**
 * @file    game.h
 * @brief   Arquivo cabeçalho com a implementacao de funcoes
            que controlam o jogo.
 * @author  Daniel Barbosa (nome@email.com)
 * @author  Jaine Budke (jainebudke@hotmail.com)
 * @since   30/05/2017
 * @date    19/06/2017
 */

#include "game.h"
#include "snake.h"

Level lv; // instanciando classe level
Snake sk; // instanciando classe snake

// ======================================================
// ACTIONS
// ======================================================

/** @brief Identifica a posicao inicial da Snake no tabuleiro.
    @return Posicao inicial da Snake */
Position Game::initialPosition(){

    // Percorre board tentando encontrar inicio
    for( int i = 0 ; i < lv.currentBoard.size() ; i++ ){        // Percorre cada pos do vector
        for( int j=0 ; j < lv.currentBoard[i].length() ; j++ ){ // Percorre cada pos da string
            if( lv.currentBoard[i][j] == '*' ){
                Position inicial;
                inicial.x = j; // coluna
            	   inicial.y = i; // linha
                lv.initial = inicial;
            	   return inicial;
            }
        }
    }

}

/** @brief Lança uma maçã no jogo, dentro das coordenadas do tabuleiro da fase.
    @return Posicao em que a maçã foi lançada */
Position Game::throwApple(){

    bool invalido = true;

    std::srand(std::time(0)); // semente do rand
    Position tamanho = sizesBoards[lv.currentLevel-1]; // pega tamanho do level atual

    // sorteia a maçã até cair num espaço livre
    do{

        int menor = 1;
        int maiorLinha  = tamanho.y -1;
        int maiorColuna = tamanho.x -1;

        Position pos;

        pos.y = rand()%(maiorLinha-menor+1) + menor;  // sorteia um numero aleatorio para a linha
        pos.x = rand()%(maiorColuna-menor+1) + menor; // sorteia um numero aleatorio para a coluna

        if( isFree(lv.currentBoard[pos.y][pos.x]) ){
            invalido = false;
            lv.currentBoard[pos.y][pos.x] = 'o';
            maca = pos;
        }

    } while( invalido );

}

/** @brief Move a cobra de acordo com as coordenadas passadas.
     @return 1 se cobra chegou na maca, bateu na parede ou rabo. 0 otherwise. */
bool Game::moveSnake(){

    Position dir = sk.listDirections[sk.currentDirection];
    sk.snake.push_front( dir );

    Position back = sk.snake.back();

    sk.snake.pop_back();

    Position front = sk.snake.front();

    lv.currentBoard[back.y][back.x] = ' ';
    lv.currentBoard[front.y][front.x] = '~';

    // se colidiu de alguma forma
    if( collideTail() or collideWall() ){
        currentState = CRASH;
        return true; // muda stop pra true
    }

    // se chegou na maçã
    if( eatingApple() ){
        currentState = EXPAND;
        lv.eatenApples += 1;
        return true; // muda stop pra true
    }

    if( sk.listDirections.size() == sk.currentDirection ){
        return true;
    }

    sk.currentDirection += 1;

    return false; // muda stop pra false - continua rodando

}

// ======================================================
// AUXILIO
// ======================================================

/** @brief Verifica se o caractere passado é uma parede.
     @return 1 se for, 0 se não for */
bool Game::isWall( char ch ){
    return ( ch == '#' );
}

/** @brief Verifica se o caractere passado é uma parede invisivel.
     @return 1 se for, 0 se não for */
bool Game::isInvisibleWall( char ch ){
    return ( ch == '.' );
}

/** @brief Verifica se o caractere passado é um lugar livre.
     @return 1 se for, 0 se não for */
bool Game::isFree( char ch ){
    return ( ch == ' ' );
}

/** @brief Verifica se o caractere passado é a posicao inicial.
     @return 1 se for, 0 se não for */
bool Game::isInitialPosition( char ch ){
    return ( ch == '*' );
}

/** @brief Verifica se a snake colidiu com o tail.
     @return 1 se colidiu, 0 otherwise */
bool Game::collideTail( ){

    // TODO

    return false;
}

/** @brief Verifica se a snake colidiu com a parede.
     @return 1 se colidiu, 0 otherwise */
bool Game::collideWall( ){

    // TODO

    return false;
}

/** @brief Verifica se a snake chegou na maca.
     @return 1 se chegou, 0 otherwise */
bool Game::eatingApple( ){

    Position pos = sk.snake.front();

    if( pos.x == maca.x and pos.y == maca.y ){
        return true;
    } else {
        return false;
    }

}

// ======================================================
// ESTADOS
// ======================================================

/** @brief Aumenta o tamanho da cobra. */
void Game::expandSnake(){

    Position pos = initialPosition();

    throwApple();

    // SE tamanho da snake for 1 ela é transformada na cobra
    if( sk.sizeSnake == 0 ){
        lv.currentBoard[pos.y][pos.x] = '~';
        sk.snake.push_front( pos );

        sk.sizeSnake += 1; // snake cresce
    }

    // SE tamanho da snake for maior que 1 ela cresce
    if( sk.sizeSnake >= 1 ){
        sk.listDirections.clear();
        sk.currentDirection = 0;
        lv.initial = sk.snake.back();
        sk.sizeSnake += 1; // snake cresce
    }

    // SE qntidade de maçãs comidas for o total -> STATE = LEVEL_UP
    if( lv.eatenApples == lv.totalApples ){
        currentState = LEVEL_UP;
    }

    // SE qntidade de maçãs comidas for menor que o total -> STATE = RUN e lança maçã
    if( lv.eatenApples < lv.totalApples ){
        currentState = RUN;
    }

}

/** @brief Faz a chamada da próxima fase do jogo setando os valores da classe Level. */
void Game::levelUp(){

    lv.currentLevel += 1; // em level é acrescentado um nível

    if( lv.currentLevel <= levels ){
        lv.currentBoard = boards[lv.currentLevel-1]; // recupera o tabuleiro do level
    }

    currentState = EXPAND;

}

/** @brief Verifica se a cobra teve alguma colisão. */
bool Game::crashSnake(){

    std::cout << "Oh no! You're crash!\n";
    currentState = DEAD;

}

/** @brief Simula a morte da cobra (diminui uma vida). */
void Game::deadSnake(){

    lives -= 1;

    std::cout << ">>> Pressione <ENTER> quando estiver pronto para continuar.";
    std::string dummy;
    std::getline( std::cin, dummy );

    currentState = RUN;

}

/** @brief A cobra anda a quantidade de vezes até chegar na maçã. */
void Game::runSnake(){

    sk.solveMaze( lv.currentBoard, lv.initial, sizesBoards[ lv.currentLevel - 1 ], maca );

    bool stop = moveSnake();

    // verifica se a condicao de parada foi acionada
    if( stop == false ){
        currentState = RUN;
    }

}

// ======================================================
// SETTERS AND GETTERS
// ======================================================

/** @brief Define a quantidade de fases do jogo.
    @param levels_ As fases
    @return True se tiver um numero de fases maior que zero; False otherwise. */
bool Game::setLevels( int levels_ ){
	if( levels_ > 0 ){
		levels = levels_;
		return true;
	} else {
		return false;
	}
}

/** @brief Recupera a quantidade de fases do jogo.
    @return As fases. */
int Game::getLevels( void ) const{
	return levels;
}

/** @brief Define um vetor com os tabuleiros do jogo.
    @param boards_ Os tabuleiros
    @return True se tiver um numero de tabuleiros maior que zero; False otherwise. */
bool Game::setBoards( std::vector<std::vector<std::string>> boards_ ){
	int qntidade = boards_.size();
	if( qntidade > 0 ){
		boards = boards_;
		return true;
	} else {
		return false;
	}
}

/** @brief Recupera os tabuleiros do jogo.
    @return Os tabuleiros. */
std::vector<std::vector<std::string>> Game::getBoards( void ) const{
	return boards;
}

/** @brief Diminui uma vida do jogador.
    @return True se tinha vidas pra diminuir; False otherwise. */
bool Game::setLives( ){
	if( lives > 0 ){
		lives--;
		return true;
	} else {
		return false;
	}
}

/** @brief Recupera a quantidade de vidas do jogador.
    @return As vidas. */
int Game::getLives( void ) const{
	return lives;
}

/** @brief Atualiza status do jogador
    @param state_ O status */
void Game::setState( int state_ ){
	state = state_;
}

/** @brief Recupera o status do jogador
    @return True se jogador ganhou; False se perdeu */
bool Game::getState( void ) const{
	if( state == 1 ){
		return true;
	}else if( state == 0 ){
		return false;
	}
}

/** @brief Atualiza os tamanhos dos tabuleiros do jogo
    @param sizesBoards_ Vetor com os tamanhos */
void Game::setSizeBoards( std::vector<Position> sizesBoards_ ){
	sizesBoards = sizesBoards_;
}

/** @brief Recupera o vetor com os tamanhos */
std::vector<Position> Game::getSizeBoards( void ) const{
	return sizesBoards;
}

// ======================================================
// GETTERS AND SETTERS DA CLASSE LEVEL
// ======================================================

/** @brief Define a fase atual que o jogador se encontra.
    @param level_ A fase
    @return True se tiver um numero de fases maior que zero; False otherwise. */
bool Game::setCurrentLevel( int level_ ){
    if( level_ > 0 ){
        lv.currentLevel = level_;
        return true;
    } else {
        return false;
    }
}

/** @brief Recupera a fase atual do jogo.
    @return A fase. */
int Game::getCurrentLevel( void ) const{
    return lv.currentLevel;
}

/** @brief Recupera o tabuleiro atual do jogo.
    @return O tabuleiro. */
std::vector<std::string> Game::getCurrentBoard( void ) const{
    return lv.currentBoard;
}

/** @brief Define o tabuleiro atual do jogo.
    @param level_ O tabuleiro
    @return True se tiver um tabuleiro; False otherwise. */
bool setCurrentBoard( std::vector<std::string> tabuleiro ){
    lv.currentBoard = tabuleiro;
}

/** @brief Recupera as macas comidas do jogo.
     @return A quandiade de macas. */
int Game::getEatenApples( void ) {
    return lv.eatenApples;
}<|endoftext|>


<commit_before>/*
 * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "GameTime.h"
#include "Player.h"
#include "ScriptedGossip.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "TaskScheduler.h"

enum Say
{
    SAY_TELEPORT = 0,
    SAY_AGGRO,
    SAY_KILL,
};

enum Spells
{
    SPELL_MARK_OF_FROST       = 23182,
    SPELL_MARK_OF_FROST_AURA  = 23184,
    SPELL_AURA_OF_FROST       = 23186,
    SPELL_MANA_STORM          = 21097,
    SPELL_CHILL               = 21098,
    SPELL_FROST_BREATH        = 21099,
    SPELL_REFLECT             = 22067,
    SPELL_CLEAVE              = 19983,
    SPELL_ARCANE_VACUUM       = 21147,
    SPELL_ARCANE_VACUUM_TP    = 21150
};

class boss_azuregos : public CreatureScript
{
public:

    boss_azuregos() : CreatureScript("boss_azuregos") { }

    struct boss_azuregosAI : public ScriptedAI
    {
        boss_azuregosAI(Creature* creature) : ScriptedAI(creature)
        {
            _scheduler.SetValidator([this]
                {
                    return !me->HasUnitState(UNIT_STATE_CASTING);
                });
        }

        void Reset() override
        {
            _scheduler.CancelAll();
            me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
            me->RestoreFaction();
            me->GetMap()->DoForAllPlayers([&](Player* p)
                {
                    if (p->GetZoneId() == me->GetZoneId())
                    {

                        p->RemoveAurasDueToSpell(SPELL_AURA_OF_FROST);
                        p->RemoveAurasDueToSpell(SPELL_CHILL);
                        p->RemoveAurasDueToSpell(SPELL_FROST_BREATH);
                    }
                });
        }

        void KilledUnit(Unit* victim) override
        {
            if (victim && victim->GetTypeId() == TYPEID_PLAYER)
            {
                Talk(SAY_KILL);
                victim->CastSpell(victim, SPELL_MARK_OF_FROST, true);
            }
        }

        void EnterCombat(Unit* /*who*/) override
        {
            DoCastSelf(SPELL_MARK_OF_FROST_AURA);
            Talk(SAY_AGGRO);

            _scheduler
                .Schedule(7s, [this](TaskContext context)
                {
                    DoCastVictim(SPELL_CLEAVE);
                    context.Repeat(7s);
                })
                .Schedule(5s, 17s, [this](TaskContext context)
                {
                    DoCastRandomTarget(SPELL_MANA_STORM);
                    context.Repeat(7s, 13s);
                })
                .Schedule(10s, 30s, [this](TaskContext context)
                {
                    DoCastVictim(SPELL_CHILL);
                    context.Repeat(13s, 25s);
                })
                .Schedule(2s, 8s, [this](TaskContext context)
                {
                    DoCastVictim(SPELL_FROST_BREATH);
                    context.Repeat(10s, 15s);
                })
                .Schedule(30s, [this](TaskContext context)
                {
                    Talk(SAY_TELEPORT);
                    DoCastAOE(SPELL_ARCANE_VACUUM);
                    context.Repeat(30s);
                })
                .Schedule(15s, 30s, [this](TaskContext context)
                {
                    DoCastSelf(SPELL_REFLECT);
                    context.Repeat(20s, 35s);
                });
        }

        void JustDied(Unit* /*killer*/) override
        {
            me->RemoveAurasDueToSpell(SPELL_MARK_OF_FROST);
            me->GetMap()->DoForAllPlayers([&](Player* p)
                {
                    if (p->GetZoneId() == me->GetZoneId())
                    {

                        p->RemoveAurasDueToSpell(SPELL_MARK_OF_FROST);
                        p->RemoveAurasDueToSpell(SPELL_AURA_OF_FROST);
                        p->RemoveAurasDueToSpell(SPELL_CHILL);
                        p->RemoveAurasDueToSpell(SPELL_FROST_BREATH);
                    }
                });

            me->SetRespawnTime(urand(2 * DAY, 3 * DAY));
            me->SaveRespawnTime();
        }

        void UpdateAI(uint32 diff) override
        {
            if (!UpdateVictim())
            {
                return;
            }

            _scheduler.Update(diff, [this]
                {
                    DoMeleeAttackIfReady();
                });
        }

        protected:
            TaskScheduler _scheduler;
    };

    bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override
    {
        CloseGossipMenuFor(player);
        creature->SetFaction(FACTION_ENEMY);
        creature->AI()->AttackStart(player);
        return true;
    }

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new boss_azuregosAI(creature);
    }
};

// Arcane Vacuum: 21147
class spell_arcane_vacuum : public SpellScript
{
    PrepareSpellScript(spell_arcane_vacuum);

    bool Validate(SpellInfo const* /*spellInfo*/) override
    {
        return ValidateSpellInfo({ SPELL_ARCANE_VACUUM_TP });
    }

    void HandleOnHit()
    {
        Unit* caster = GetCaster();
        Unit* hitUnit = GetHitUnit();
        if (caster && hitUnit && hitUnit->ToPlayer())
        {
            caster->GetThreatMgr().modifyThreatPercent(hitUnit, -100);
            caster->CastSpell(hitUnit, SPELL_ARCANE_VACUUM_TP, true);
        }
    }

    void Register() override
    {
        OnHit += SpellHitFn(spell_arcane_vacuum::HandleOnHit);
    }
};

// Mark of Frost - Triggered Spell
class spell_mark_of_frost_freeze : public SpellScript
{
    PrepareSpellScript(spell_mark_of_frost_freeze);

    bool Validate(SpellInfo const* /*spellInfo*/) override
    {
        return ValidateSpellInfo({ SPELL_MARK_OF_FROST, SPELL_AURA_OF_FROST });
    }

    void HandleOnHit()
    {
        Unit* caster = GetCaster();
        Unit* hitUnit = GetHitUnit();
        if (caster && hitUnit && hitUnit->HasAura(SPELL_MARK_OF_FROST) && !hitUnit->HasAura(SPELL_AURA_OF_FROST))
        {
            hitUnit->CastSpell(hitUnit, SPELL_AURA_OF_FROST, true);
        }
    }

    void Register() override
    {
        OnHit += SpellHitFn(spell_mark_of_frost_freeze::HandleOnHit);
    }
};

void AddSC_boss_azuregos()
{
    new boss_azuregos();
    RegisterSpellScript(spell_arcane_vacuum);
    RegisterSpellScript(spell_mark_of_frost_freeze);
}
<commit_msg>fix(Scripts/Azuregos): mark of frost should be removed on reset (#12053)<commit_after>/*
 * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by the
 * Free Software Foundation; either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "GameTime.h"
#include "Player.h"
#include "ScriptedGossip.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "TaskScheduler.h"

enum Say
{
    SAY_TELEPORT = 0,
    SAY_AGGRO,
    SAY_KILL,
};

enum Spells
{
    SPELL_MARK_OF_FROST       = 23182,
    SPELL_MARK_OF_FROST_AURA  = 23184,
    SPELL_AURA_OF_FROST       = 23186,
    SPELL_MANA_STORM          = 21097,
    SPELL_CHILL               = 21098,
    SPELL_FROST_BREATH        = 21099,
    SPELL_REFLECT             = 22067,
    SPELL_CLEAVE              = 19983,
    SPELL_ARCANE_VACUUM       = 21147,
    SPELL_ARCANE_VACUUM_TP    = 21150
};

class boss_azuregos : public CreatureScript
{
public:

    boss_azuregos() : CreatureScript("boss_azuregos") { }

    struct boss_azuregosAI : public ScriptedAI
    {
        boss_azuregosAI(Creature* creature) : ScriptedAI(creature)
        {
            _scheduler.SetValidator([this]
                {
                    return !me->HasUnitState(UNIT_STATE_CASTING);
                });
        }

        void Reset() override
        {
            _scheduler.CancelAll();
            me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
            me->RestoreFaction();
            me->GetMap()->DoForAllPlayers([&](Player* p)
                {
                    if (p->GetZoneId() == me->GetZoneId())
                    {
                        p->RemoveAurasDueToSpell(SPELL_CHILL);
                        p->RemoveAurasDueToSpell(SPELL_FROST_BREATH);
                    }
                });
        }

        void KilledUnit(Unit* victim) override
        {
            if (victim && victim->GetTypeId() == TYPEID_PLAYER)
            {
                Talk(SAY_KILL);
                victim->CastSpell(victim, SPELL_MARK_OF_FROST, true);
            }
        }

        void EnterCombat(Unit* /*who*/) override
        {
            DoCastSelf(SPELL_MARK_OF_FROST_AURA);
            Talk(SAY_AGGRO);

            _scheduler
                .Schedule(7s, [this](TaskContext context)
                {
                    DoCastVictim(SPELL_CLEAVE);
                    context.Repeat(7s);
                })
                .Schedule(5s, 17s, [this](TaskContext context)
                {
                    DoCastRandomTarget(SPELL_MANA_STORM);
                    context.Repeat(7s, 13s);
                })
                .Schedule(10s, 30s, [this](TaskContext context)
                {
                    DoCastVictim(SPELL_CHILL);
                    context.Repeat(13s, 25s);
                })
                .Schedule(2s, 8s, [this](TaskContext context)
                {
                    DoCastVictim(SPELL_FROST_BREATH);
                    context.Repeat(10s, 15s);
                })
                .Schedule(30s, [this](TaskContext context)
                {
                    Talk(SAY_TELEPORT);
                    DoCastAOE(SPELL_ARCANE_VACUUM);
                    context.Repeat(30s);
                })
                .Schedule(15s, 30s, [this](TaskContext context)
                {
                    DoCastSelf(SPELL_REFLECT);
                    context.Repeat(20s, 35s);
                });
        }

        void JustDied(Unit* /*killer*/) override
        {
            me->RemoveAurasDueToSpell(SPELL_MARK_OF_FROST);
            me->GetMap()->DoForAllPlayers([&](Player* p)
                {
                    if (p->GetZoneId() == me->GetZoneId())
                    {

                        p->RemoveAurasDueToSpell(SPELL_MARK_OF_FROST);
                        p->RemoveAurasDueToSpell(SPELL_AURA_OF_FROST);
                        p->RemoveAurasDueToSpell(SPELL_CHILL);
                        p->RemoveAurasDueToSpell(SPELL_FROST_BREATH);
                    }
                });

            me->SetRespawnTime(urand(2 * DAY, 3 * DAY));
            me->SaveRespawnTime();
        }

        void UpdateAI(uint32 diff) override
        {
            if (!UpdateVictim())
            {
                return;
            }

            _scheduler.Update(diff, [this]
                {
                    DoMeleeAttackIfReady();
                });
        }

        protected:
            TaskScheduler _scheduler;
    };

    bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override
    {
        CloseGossipMenuFor(player);
        creature->SetFaction(FACTION_ENEMY);
        creature->AI()->AttackStart(player);
        return true;
    }

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new boss_azuregosAI(creature);
    }
};

// Arcane Vacuum: 21147
class spell_arcane_vacuum : public SpellScript
{
    PrepareSpellScript(spell_arcane_vacuum);

    bool Validate(SpellInfo const* /*spellInfo*/) override
    {
        return ValidateSpellInfo({ SPELL_ARCANE_VACUUM_TP });
    }

    void HandleOnHit()
    {
        Unit* caster = GetCaster();
        Unit* hitUnit = GetHitUnit();
        if (caster && hitUnit && hitUnit->ToPlayer())
        {
            caster->GetThreatMgr().modifyThreatPercent(hitUnit, -100);
            caster->CastSpell(hitUnit, SPELL_ARCANE_VACUUM_TP, true);
        }
    }

    void Register() override
    {
        OnHit += SpellHitFn(spell_arcane_vacuum::HandleOnHit);
    }
};

// Mark of Frost - Triggered Spell
class spell_mark_of_frost_freeze : public SpellScript
{
    PrepareSpellScript(spell_mark_of_frost_freeze);

    bool Validate(SpellInfo const* /*spellInfo*/) override
    {
        return ValidateSpellInfo({ SPELL_MARK_OF_FROST, SPELL_AURA_OF_FROST });
    }

    void HandleOnHit()
    {
        Unit* caster = GetCaster();
        Unit* hitUnit = GetHitUnit();
        if (caster && hitUnit && hitUnit->HasAura(SPELL_MARK_OF_FROST) && !hitUnit->HasAura(SPELL_AURA_OF_FROST))
        {
            hitUnit->CastSpell(hitUnit, SPELL_AURA_OF_FROST, true);
        }
    }

    void Register() override
    {
        OnHit += SpellHitFn(spell_mark_of_frost_freeze::HandleOnHit);
    }
};

void AddSC_boss_azuregos()
{
    new boss_azuregos();
    RegisterSpellScript(spell_arcane_vacuum);
    RegisterSpellScript(spell_mark_of_frost_freeze);
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <windows.h>
#include <mmsystem.h>
#include <process.h>

#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

class MockTimeTicks : public TimeTicks {
 public:
  static DWORD Ticker() {
    return static_cast<int>(InterlockedIncrement(&ticker_));
  }

  static void InstallTicker() {
    old_tick_function_ = SetMockTickFunction(&Ticker);
    ticker_ = -5;
  }

  static void UninstallTicker() {
    SetMockTickFunction(old_tick_function_);
  }

 private:
  static volatile LONG ticker_;
  static TickFunctionType old_tick_function_;
};

volatile LONG MockTimeTicks::ticker_;
MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;

HANDLE g_rollover_test_start;

unsigned __stdcall RolloverTestThreadMain(void* param) {
  int64 counter = reinterpret_cast<int64>(param);
  DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
  EXPECT_EQ(rv, WAIT_OBJECT_0);

  TimeTicks last = TimeTicks::Now();
  for (int index = 0; index < counter; index++) {
    TimeTicks now = TimeTicks::Now();
    int64 milliseconds = (now - last).InMilliseconds();
    // This is a tight loop; we could have looped faster than our
    // measurements, so the time might be 0 millis.
    EXPECT_GE(milliseconds, 0);
    EXPECT_LT(milliseconds, 250);
    last = now;
  }
  return 0;
}

}  // namespace

TEST(TimeTicks, WinRollover) {
  // The internal counter rolls over at ~49days.  We'll use a mock
  // timer to test this case.
  // Basic test algorithm:
  //   1) Set clock to rollover - N
  //   2) Create N threads
  //   3) Start the threads
  //   4) Each thread loops through TimeTicks() N times
  //   5) Each thread verifies integrity of result.

  const int kThreads = 8;
  // Use int64 so we can cast into a void* without a compiler warning.
  const int64 kChecks = 10;

  // It takes a lot of iterations to reproduce the bug!
  // (See bug 1081395)
  for (int loop = 0; loop < 4096; loop++) {
    // Setup
    MockTimeTicks::InstallTicker();
    g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
    HANDLE threads[kThreads];

    for (int index = 0; index < kThreads; index++) {
      void* argument = reinterpret_cast<void*>(kChecks);
      unsigned thread_id;
      threads[index] = reinterpret_cast<HANDLE>(
        _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
          &thread_id));
      EXPECT_NE((HANDLE)NULL, threads[index]);
    }

    // Start!
    SetEvent(g_rollover_test_start);

    // Wait for threads to finish
    for (int index = 0; index < kThreads; index++) {
      DWORD rv = WaitForSingleObject(threads[index], INFINITE);
      EXPECT_EQ(rv, WAIT_OBJECT_0);
    }

    CloseHandle(g_rollover_test_start);

    // Teardown
    MockTimeTicks::UninstallTicker();
  }
}

TEST(TimeTicks, SubMillisecondTimers) {
  // Loop for a bit getting timers quickly.  We want to
  // see at least one case where we get a new sample in
  // less than one millisecond.
  bool saw_submillisecond_timer = false;
  int64 min_timer = 1000;
  TimeTicks last_time = TimeTicks::HighResNow();
  for (int index = 0; index < 1000; index++) {
    TimeTicks now = TimeTicks::HighResNow();
    TimeDelta delta = now - last_time;
    if (delta.InMicroseconds() > 0 &&
        delta.InMicroseconds() < 1000) {
      if (min_timer > delta.InMicroseconds())
        min_timer = delta.InMicroseconds();
      saw_submillisecond_timer = true;
    }
    last_time = now;
  }
  EXPECT_TRUE(saw_submillisecond_timer);
  printf("Min timer is: %dus\n", min_timer);
}

TEST(TimeTicks, TimeGetTimeCaps) {
  // Test some basic assumptions that we expect about how timeGetDevCaps works.

  TIMECAPS caps;
  MMRESULT status = timeGetDevCaps(&caps, sizeof(caps));
  EXPECT_EQ(TIMERR_NOERROR, status);
  if (status != TIMERR_NOERROR) {
    printf("Could not get timeGetDevCaps\n");
    return;
  }

  EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
  EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
  EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
  EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
  printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin,
    caps.wPeriodMax);
}

TEST(TimeTicks, QueryPerformanceFrequency) {
  // Test some basic assumptions that we expect about QPC.

  LARGE_INTEGER frequency;
  BOOL rv = QueryPerformanceFrequency(&frequency);
  EXPECT_EQ(TRUE, rv);
  EXPECT_GT(frequency.QuadPart, 1000000);  // Expect at least 1MHz
  printf("QueryPerformanceFrequency is %5.2fMHz\n",
    frequency.QuadPart / 1000000.0);
}

TEST(TimeTicks, TimerPerformance) {
  // Verify that various timer mechanisms can always complete quickly.
  // Note:  This is a somewhat arbitrary test.
  const int kLoops = 10000;
  const int kMaxTime = 10;  // Maximum acceptible milliseconds for test.

  typedef TimeTicks (*TestFunc)();
  struct TestCase {
    TestFunc func;
    char *description;
  };
  // Cheating a bit here:  assumes sizeof(TimeTicks) == sizeof(Time)
  // in order to create a single test case list.
  COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time),
                 test_only_works_with_same_sizes);
  TestCase cases[] = {
    { reinterpret_cast<TestFunc>(Time::Now), "Time::Now" },
    { TimeTicks::Now, "TimeTicks::Now" },
    { TimeTicks::HighResNow, "TimeTicks::HighResNow" },
    { NULL, "" }
  };

  int test_case = 0;
  while (cases[test_case].func) {
    TimeTicks start = TimeTicks::HighResNow();
    for (int index = 0; index < kLoops; index++)
      cases[test_case].func();
    TimeTicks stop = TimeTicks::HighResNow();
    EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
    printf("%s: %1.2fus per call\n", cases[test_case].description,
      (stop - start).InMillisecondsF() * 1000 / kLoops);
    test_case++;
  }
}
<commit_msg>Increase min throttle on the new timer performance test. This is an arbitrary timer, but these tests run slower than I expected on the bbots.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <windows.h>
#include <mmsystem.h>
#include <process.h>

#include "base/time.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

class MockTimeTicks : public TimeTicks {
 public:
  static DWORD Ticker() {
    return static_cast<int>(InterlockedIncrement(&ticker_));
  }

  static void InstallTicker() {
    old_tick_function_ = SetMockTickFunction(&Ticker);
    ticker_ = -5;
  }

  static void UninstallTicker() {
    SetMockTickFunction(old_tick_function_);
  }

 private:
  static volatile LONG ticker_;
  static TickFunctionType old_tick_function_;
};

volatile LONG MockTimeTicks::ticker_;
MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_;

HANDLE g_rollover_test_start;

unsigned __stdcall RolloverTestThreadMain(void* param) {
  int64 counter = reinterpret_cast<int64>(param);
  DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
  EXPECT_EQ(rv, WAIT_OBJECT_0);

  TimeTicks last = TimeTicks::Now();
  for (int index = 0; index < counter; index++) {
    TimeTicks now = TimeTicks::Now();
    int64 milliseconds = (now - last).InMilliseconds();
    // This is a tight loop; we could have looped faster than our
    // measurements, so the time might be 0 millis.
    EXPECT_GE(milliseconds, 0);
    EXPECT_LT(milliseconds, 250);
    last = now;
  }
  return 0;
}

}  // namespace

TEST(TimeTicks, WinRollover) {
  // The internal counter rolls over at ~49days.  We'll use a mock
  // timer to test this case.
  // Basic test algorithm:
  //   1) Set clock to rollover - N
  //   2) Create N threads
  //   3) Start the threads
  //   4) Each thread loops through TimeTicks() N times
  //   5) Each thread verifies integrity of result.

  const int kThreads = 8;
  // Use int64 so we can cast into a void* without a compiler warning.
  const int64 kChecks = 10;

  // It takes a lot of iterations to reproduce the bug!
  // (See bug 1081395)
  for (int loop = 0; loop < 4096; loop++) {
    // Setup
    MockTimeTicks::InstallTicker();
    g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
    HANDLE threads[kThreads];

    for (int index = 0; index < kThreads; index++) {
      void* argument = reinterpret_cast<void*>(kChecks);
      unsigned thread_id;
      threads[index] = reinterpret_cast<HANDLE>(
        _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
          &thread_id));
      EXPECT_NE((HANDLE)NULL, threads[index]);
    }

    // Start!
    SetEvent(g_rollover_test_start);

    // Wait for threads to finish
    for (int index = 0; index < kThreads; index++) {
      DWORD rv = WaitForSingleObject(threads[index], INFINITE);
      EXPECT_EQ(rv, WAIT_OBJECT_0);
    }

    CloseHandle(g_rollover_test_start);

    // Teardown
    MockTimeTicks::UninstallTicker();
  }
}

TEST(TimeTicks, SubMillisecondTimers) {
  // Loop for a bit getting timers quickly.  We want to
  // see at least one case where we get a new sample in
  // less than one millisecond.
  bool saw_submillisecond_timer = false;
  int64 min_timer = 1000;
  TimeTicks last_time = TimeTicks::HighResNow();
  for (int index = 0; index < 1000; index++) {
    TimeTicks now = TimeTicks::HighResNow();
    TimeDelta delta = now - last_time;
    if (delta.InMicroseconds() > 0 &&
        delta.InMicroseconds() < 1000) {
      if (min_timer > delta.InMicroseconds())
        min_timer = delta.InMicroseconds();
      saw_submillisecond_timer = true;
    }
    last_time = now;
  }
  EXPECT_TRUE(saw_submillisecond_timer);
  printf("Min timer is: %dus\n", min_timer);
}

TEST(TimeTicks, TimeGetTimeCaps) {
  // Test some basic assumptions that we expect about how timeGetDevCaps works.

  TIMECAPS caps;
  MMRESULT status = timeGetDevCaps(&caps, sizeof(caps));
  EXPECT_EQ(TIMERR_NOERROR, status);
  if (status != TIMERR_NOERROR) {
    printf("Could not get timeGetDevCaps\n");
    return;
  }

  EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
  EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
  EXPECT_GE(static_cast<int>(caps.wPeriodMin), 1);
  EXPECT_GT(static_cast<int>(caps.wPeriodMax), 1);
  printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin,
    caps.wPeriodMax);
}

TEST(TimeTicks, QueryPerformanceFrequency) {
  // Test some basic assumptions that we expect about QPC.

  LARGE_INTEGER frequency;
  BOOL rv = QueryPerformanceFrequency(&frequency);
  EXPECT_EQ(TRUE, rv);
  EXPECT_GT(frequency.QuadPart, 1000000);  // Expect at least 1MHz
  printf("QueryPerformanceFrequency is %5.2fMHz\n",
    frequency.QuadPart / 1000000.0);
}

TEST(TimeTicks, TimerPerformance) {
  // Verify that various timer mechanisms can always complete quickly.
  // Note:  This is a somewhat arbitrary test.
  const int kLoops = 10000;
  // Due to the fact that these run on bbots, which are horribly slow,
  // we can't really make any guarantees about minimum runtime.
  // Really, we want these to finish in ~10ms, and that is generous.
  const int kMaxTime = 35;  // Maximum acceptible milliseconds for test.

  typedef TimeTicks (*TestFunc)();
  struct TestCase {
    TestFunc func;
    char *description;
  };
  // Cheating a bit here:  assumes sizeof(TimeTicks) == sizeof(Time)
  // in order to create a single test case list.
  COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time),
                 test_only_works_with_same_sizes);
  TestCase cases[] = {
    { reinterpret_cast<TestFunc>(Time::Now), "Time::Now" },
    { TimeTicks::Now, "TimeTicks::Now" },
    { TimeTicks::HighResNow, "TimeTicks::HighResNow" },
    { NULL, "" }
  };

  int test_case = 0;
  while (cases[test_case].func) {
    TimeTicks start = TimeTicks::HighResNow();
    for (int index = 0; index < kLoops; index++)
      cases[test_case].func();
    TimeTicks stop = TimeTicks::HighResNow();
    EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
    printf("%s: %1.2fus per call\n", cases[test_case].description,
      (stop - start).InMillisecondsF() * 1000 / kLoops);
    test_case++;
  }
}
<|endoftext|>