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;
|
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>
|
template<typename Floating>
|
||||||
bool isAlmostZero(const Floating & value)
|
bool isAlmostZero(const Floating & value)
|
||||||
{
|
{
|
||||||
|
@ -145,7 +145,8 @@ int DamageCalculator::getActorAttackIgnored() const
|
|||||||
|
|
||||||
if(multAttackReductionPercent > 0)
|
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 -std::min(reduction, getActorAttackBase());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -227,7 +227,7 @@ TResources CGTownInstance::dailyIncome() const
|
|||||||
auto playerSettings = cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner());
|
auto playerSettings = cb->gameState()->scenarioOps->getIthPlayersSettings(getOwner());
|
||||||
for(TResources::nziterator it(ret); it.valid(); it++)
|
for(TResources::nziterator it(ret); it.valid(); it++)
|
||||||
// always round up income - we don't want to always produce zero if handicap in use
|
// 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1271,7 +1271,7 @@ int GrowthInfo::totalGrowth() const
|
|||||||
ret += entry.count;
|
ret += entry.count;
|
||||||
|
|
||||||
// always round up income - we don't want buildings to always produce zero if handicap in use
|
// 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
|
void CGTownInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const
|
||||||
|
@ -200,7 +200,7 @@ ui32 CGMine::getProducedQuantity() const
|
|||||||
{
|
{
|
||||||
auto * playerSettings = cb->getPlayerSettings(getOwner());
|
auto * playerSettings = cb->getPlayerSettings(getOwner());
|
||||||
// always round up income - we don't want mines to always produce zero if handicap in use
|
// 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
|
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%
|
vstd::amin(chanceToKill, 1); //cap at 100%
|
||||||
int killedCreatures = gameHandler->getRandomGenerator().nextBinomialInt(attacker->getCount(), chanceToKill);
|
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);
|
vstd::amin(killedCreatures, maxToKill);
|
||||||
|
|
||||||
killedCreatures += (attacker->level() * attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareCommander)) / defender->level();
|
killedCreatures += (attacker->level() * attacker->valOfBonuses(BonusType::DEATH_STARE, BonusCustomSubtype::deathStareCommander)) / defender->level();
|
||||||
|
Loading…
Reference in New Issue
Block a user