mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Add helper functions for integer division rounding
Added set of functions that perform integer division with different rounding modes: - divideAndCeil - rounds up to next integer - divideAndRound - rounds to nearest integer - divideAndFloor - rounds to previous integer (equivalent to default division) Intended for use in library, where usage of floating point might lead to desync in multiplayer games. Replaced some cases that I knew of, including recent handicap PR
This commit is contained in:
parent
257fb8c70c
commit
81e6207df0
27
Global.h
27
Global.h
@ -700,6 +700,33 @@ namespace vstd
|
||||
return a + (b - a) * f;
|
||||
}
|
||||
|
||||
/// Divides dividend by divisor and rounds result up
|
||||
/// For use with integer-only arithmetic
|
||||
template<typename Integer1, typename Integer2>
|
||||
Integer1 divideAndCeil(const Integer1 & dividend, const Integer2 & divisor)
|
||||
{
|
||||
static_assert(std::is_integral_v<Integer1> && std::is_integral_v<Integer2>, "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<typename Integer1, typename Integer2>
|
||||
Integer1 divideAndRound(const Integer1 & dividend, const Integer2 & divisor)
|
||||
{
|
||||
static_assert(std::is_integral_v<Integer1> && std::is_integral_v<Integer2>, "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<typename Integer1, typename Integer2>
|
||||
Integer1 divideAndFloor(const Integer1 & dividend, const Integer2 & divisor)
|
||||
{
|
||||
static_assert(std::is_integral_v<Integer1> && std::is_integral_v<Integer2>, "This function should only be used with integral types");
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
template<typename Floating>
|
||||
bool isAlmostZero(const Floating & value)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user