diff --git a/AI/BattleAI/AttackPossibility.cpp b/AI/BattleAI/AttackPossibility.cpp index af36ed07a..b2d4c769f 100644 --- a/AI/BattleAI/AttackPossibility.cpp +++ b/AI/BattleAI/AttackPossibility.cpp @@ -62,16 +62,12 @@ void DamageCache::buildDamageCache(std::shared_ptr hb, int sid int64_t DamageCache::getDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr hb) { - auto damage = damageCache[attacker->unitId()][defender->unitId()] * attacker->getCount(); + bool wasComputedBefore = damageCache[attacker->unitId()].count(defender->unitId()); - if(damage == 0) - { + if (!wasComputedBefore) cacheDamage(attacker, defender, hb); - damage = damageCache[attacker->unitId()][defender->unitId()] * attacker->getCount(); - } - - return static_cast(damage); + return damageCache[attacker->unitId()][defender->unitId()] * attacker->getCount(); } int64_t DamageCache::getOriginalDamage(const battle::Unit * attacker, const battle::Unit * defender, std::shared_ptr hb) diff --git a/AI/BattleAI/BattleExchangeVariant.cpp b/AI/BattleAI/BattleExchangeVariant.cpp index f0a29682b..d7902f878 100644 --- a/AI/BattleAI/BattleExchangeVariant.cpp +++ b/AI/BattleAI/BattleExchangeVariant.cpp @@ -270,7 +270,7 @@ EvaluationResult BattleExchangeEvaluator::findBestTarget( { float score = evaluateExchange(ap, 0, targets, damageCache, hb); - if(score > result.score || (score == result.score && result.wait)) + if(score > result.score || (vstd::isAlmostEqual(score, result.score) && result.wait)) { result.score = score; result.bestAttack = ap; diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 9ed9fe77f..1fced0f95 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -632,7 +632,8 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vectorid == hero->id ? objects.back() : objects.front(); auto objType = topObj->ID; // top object should be our hero auto goalObjectID = nullkiller->getTargetObject(); - auto ratio = (float)nullkiller->dangerEvaluator->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength(); + auto danger = nullkiller->dangerEvaluator->evaluateDanger(target, hero.get()); + auto ratio = static_cast(danger) / hero->getTotalStrength(); answer = topObj->id == goalObjectID; // no if we do not aim to visit this object logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name, ratio); @@ -648,7 +649,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector (1 / SAFE_ATTACK_CONSTANT); answer = !dangerUnknown && !dangerTooHigh; diff --git a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp index ccf6b7ecb..76ac640f7 100644 --- a/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp +++ b/AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp @@ -226,7 +226,7 @@ void DangerHitMapAnalyzer::calculateTileOwners() } } - if(ourDistance == enemyDistance) + if(vstd::isAlmostEqual(ourDistance, enemyDistance)) { hitMap[pos.x][pos.y][pos.z].closestTown = nullptr; } diff --git a/Global.h b/Global.h index 7135412b1..43ee6d7ec 100644 --- a/Global.h +++ b/Global.h @@ -685,6 +685,19 @@ namespace vstd return a + (b - a) * f; } + template + bool isAlmostZero(const Floating & value) + { + constexpr Floating epsilon(0.00001); + return std::abs(value) < epsilon; + } + + template + bool isAlmostEqual(const Floating1 & left, const Floating2 & right) + { + return isAlmostZero(left - right); + } + ///compile-time version of std::abs for ints for int3, in clang++15 std::abs is constexpr static constexpr int abs(int i) { if(i < 0) return -i; diff --git a/client/mapView/MapViewCache.cpp b/client/mapView/MapViewCache.cpp index ad851ef11..2bd21ce8d 100644 --- a/client/mapView/MapViewCache.cpp +++ b/client/mapView/MapViewCache.cpp @@ -141,7 +141,7 @@ void MapViewCache::update(const std::shared_ptr & context) void MapViewCache::render(const std::shared_ptr & context, Canvas & target, bool fullRedraw) { bool mapMoved = (cachedPosition != model->getMapViewCenter()); - bool lazyUpdate = !mapMoved && !fullRedraw && context->viewTransitionProgress() == 0; + bool lazyUpdate = !mapMoved && !fullRedraw && vstd::isAlmostZero(context->viewTransitionProgress()); Rect dimensions = model->getTilesTotalRect(); @@ -184,7 +184,7 @@ void MapViewCache::render(const std::shared_ptr & context, } } - if(context->viewTransitionProgress() != 0) + if(!vstd::isAlmostZero(context->viewTransitionProgress())) target.drawTransparent(*terrainTransition, Point(0, 0), 1.0 - context->viewTransitionProgress()); cachedPosition = model->getMapViewCenter(); diff --git a/client/render/ColorFilter.cpp b/client/render/ColorFilter.cpp index d6234116f..4540c0557 100644 --- a/client/render/ColorFilter.cpp +++ b/client/render/ColorFilter.cpp @@ -41,10 +41,10 @@ bool ColorFilter::operator != (const ColorFilter & other) const bool ColorFilter::operator == (const ColorFilter & other) const { return - r.r == other.r.r && r.g && other.r.g && r.b == other.r.b && r.a == other.r.a && - g.r == other.g.r && g.g && other.g.g && g.b == other.g.b && g.a == other.g.a && - b.r == other.b.r && b.g && other.b.g && b.b == other.b.b && b.a == other.b.a && - a == other.a; + vstd::isAlmostEqual(r.r, other.r.r) && vstd::isAlmostEqual(r.g, other.r.g) && vstd::isAlmostEqual(r.b, other.r.b) && vstd::isAlmostEqual(r.a, other.r.a) && + vstd::isAlmostEqual(g.r, other.g.r) && vstd::isAlmostEqual(g.g, other.g.g) && vstd::isAlmostEqual(g.b, other.g.b) && vstd::isAlmostEqual(g.a, other.g.a) && + vstd::isAlmostEqual(b.r, other.b.r) && vstd::isAlmostEqual(b.g, other.b.g) && vstd::isAlmostEqual(b.b, other.b.b) && vstd::isAlmostEqual(b.a, other.b.a) && + vstd::isAlmostEqual(a, other.a); } ColorFilter ColorFilter::genEmptyShifter( ) diff --git a/client/widgets/Slider.cpp b/client/widgets/Slider.cpp index 8a37b6f46..dff5d3229 100644 --- a/client/widgets/Slider.cpp +++ b/client/widgets/Slider.cpp @@ -21,23 +21,24 @@ void CSlider::mouseDragged(const Point & cursorPosition, const Point & lastUpdateDistance) { - double v = 0; + double newPosition = 0; if(getOrientation() == Orientation::HORIZONTAL) { - v = cursorPosition.x - pos.x - 24; - v *= positions; - v /= (pos.w - 48); + newPosition = cursorPosition.x - pos.x - 24; + newPosition *= positions; + newPosition /= (pos.w - 48); } else { - v = cursorPosition.y - pos.y - 24; - v *= positions; - v /= (pos.h - 48); + newPosition = cursorPosition.y - pos.y - 24; + newPosition *= positions; + newPosition /= (pos.h - 48); } - v += 0.5; - if(v!=value) + + int positionInteger = std::round(newPosition); + if(positionInteger != value) { - scrollTo(static_cast(v)); + scrollTo(static_cast(newPosition)); } } diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 87889fe1d..530c3c45b 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -844,8 +844,8 @@ void CCreatureHandler::loadUnitAnimInfo(JsonNode & graphics, CLegacyConfigParser missile["attackClimaxFrame"].Float() = parser.readNumber(); // assume that creature is not a shooter and should not have whole missile field - if (missile["frameAngles"].Vector()[0].Float() == 0 && - missile["attackClimaxFrame"].Float() == 0) + if (missile["frameAngles"].Vector()[0].Integer() == 0 && + missile["attackClimaxFrame"].Integer() == 0) graphics.Struct().erase("missile"); } @@ -987,10 +987,10 @@ void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode int lastVal = 0; for (const JsonNode &val : values) { - if (val.Float() != lastVal) + if (val.Integer() != lastVal) { JsonNode bonusInput = exp["bonus"]; - bonusInput["val"].Float() = static_cast(val.Float()) - lastVal; + bonusInput["val"].Float() = val.Integer() - lastVal; auto bonus = JsonUtils::parseBonus (bonusInput); bonus->source = BonusSource::STACK_EXPERIENCE; diff --git a/lib/rmg/float3.h b/lib/rmg/float3.h index 97bbc95fc..ca8775c3a 100644 --- a/lib/rmg/float3.h +++ b/lib/rmg/float3.h @@ -113,23 +113,6 @@ public: return *this; } - bool operator==(const float3 & i) const { return (x == i.x) && (y == i.y) && (z == i.z); } - bool operator!=(const float3 & i) const { return (x != i.x) || (y != i.y) || (z != i.z); } - - bool operator<(const float3 & i) const - { - if (zi.z) - return false; - if (yi.y) - return false; - - return x