diff --git a/Global.h b/Global.h index cfa07cc90..6fa656a17 100644 --- a/Global.h +++ b/Global.h @@ -706,6 +706,33 @@ namespace vstd return a + (b - a) * f; } + /// Divides dividend by divisor and rounds result up + /// For use with integer-only arithmetic + template + Integer1 divideAndCeil(const Integer1 & dividend, const Integer2 & divisor) + { + static_assert(std::is_integral_v && std::is_integral_v, "This function should only be used with integral types"); + return (dividend + divisor - 1) / divisor; + } + + /// Divides dividend by divisor and rounds result to nearest + /// For use with integer-only arithmetic + template + Integer1 divideAndRound(const Integer1 & dividend, const Integer2 & divisor) + { + static_assert(std::is_integral_v && std::is_integral_v, "This function should only be used with integral types"); + return (dividend + divisor / 2 - 1) / divisor; + } + + /// Divides dividend by divisor and rounds result down + /// For use with integer-only arithmetic + template + Integer1 divideAndFloor(const Integer1 & dividend, const Integer2 & divisor) + { + static_assert(std::is_integral_v && std::is_integral_v, "This function should only be used with integral types"); + return dividend / divisor; + } + template bool isAlmostZero(const Floating & value) { diff --git a/lib/battle/DamageCalculator.cpp b/lib/battle/DamageCalculator.cpp index 99bce45e6..0bcc88ab0 100644 --- a/lib/battle/DamageCalculator.cpp +++ b/lib/battle/DamageCalculator.cpp @@ -145,7 +145,8 @@ int DamageCalculator::getActorAttackIgnored() const if(multAttackReductionPercent > 0) { - int reduction = (getActorAttackBase() * multAttackReductionPercent + 49) / 100; //using ints so 1.5 for 5 attack is rounded down as in HotA / h3assist etc. (keep in mind h3assist 1.2 shows wrong value for 15 attack points and unupg. nix) + //using ints so 1.5 for 5 attack is rounded down as in HotA / h3assist etc. (keep in mind h3assist 1.2 shows wrong value for 15 attack points and unupg. nix) + int reduction = vstd::divideAndRound( getActorAttackBase() * multAttackReductionPercent, 100); return -std::min(reduction, getActorAttackBase()); } return 0; diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index bec0aebc7..6f646bf87 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -227,7 +227,7 @@ TResources CGTownInstance::dailyIncome() const auto playerSettings = cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner()); for(TResources::nziterator it(ret); it.valid(); it++) // always round up income - we don't want to always produce zero if handicap in use - ret[it->resType] = (ret[it->resType] * playerSettings.handicap.percentIncome + 99) / 100; + ret[it->resType] = vstd::divideAndCeil(ret[it->resType] * playerSettings.handicap.percentIncome, 100); return ret; } @@ -1271,7 +1271,7 @@ int GrowthInfo::totalGrowth() const ret += entry.count; // always round up income - we don't want buildings to always produce zero if handicap in use - return (ret * handicapPercentage + 99) / 100; + return vstd::divideAndCeil(ret * handicapPercentage, 100); } void CGTownInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index b0eff7592..33e96cbd7 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -200,7 +200,7 @@ ui32 CGMine::getProducedQuantity() const { auto * playerSettings = cb->getPlayerSettings(getOwner()); // always round up income - we don't want mines to always produce zero if handicap in use - return (producedQuantity * playerSettings->handicap.percentIncome + 99) / 100; + return vstd::divideAndCeil(producedQuantity * playerSettings->handicap.percentIncome, 100); } void CGMine::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index b7633021a..945ee960c 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -1267,7 +1267,7 @@ void BattleActionProcessor::handleDeathStare(const CBattleInfoCallback & battle, vstd::amin(chanceToKill, 1); //cap at 100% int killedCreatures = gameHandler->getRandomGenerator().nextBinomialInt(attacker->getCount(), chanceToKill); - int maxToKill = (attacker->getCount() * singleCreatureKillChancePercent + 99) / 100; + int maxToKill = vstd::divideAndCeil(attacker->getCount() * singleCreatureKillChancePercent, 100); vstd::amin(killedCreatures, maxToKill); killedCreatures += (attacker->level() * attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareCommander)) / defender->level();