Add a function to wrap percentage calculations
This should help avoid issues from integer overflow (which is technically undefined behaviour) while also allowing such issues to be addressed centrally if they still exist.
This commit is contained in:
@@ -2736,7 +2736,7 @@ static void run_waterfalls(short mode){ // mode 0 - town, 1 - outdoors
|
||||
add_string_to_buf(" (No supplies lost.)");
|
||||
else {
|
||||
cTerrain& ter = univ.scenario.ter_types[coord_to_ter(x, y)];
|
||||
int lost = univ.party.food * ter.flag2 / 100;
|
||||
int lost = percent(univ.party.food, ter.flag2);
|
||||
if(lost >= ter.flag3) {
|
||||
lost = ter.flag3;
|
||||
add_string_to_buf(" (Many supplies lost.)");
|
||||
|
@@ -3265,7 +3265,7 @@ void monst_basic_abil(short m_num, std::pair<eMonstAbil,uAbility> abil, iLiving*
|
||||
}
|
||||
break;
|
||||
case eMonstAbil::PETRIFY:
|
||||
i = univ.town.monst[m_num].level * abil.second.gen.strength / 100;
|
||||
i = percent(univ.town.monst[m_num].level, abil.second.gen.strength);
|
||||
if(pc_target != nullptr)
|
||||
petrify_pc(*pc_target, i);
|
||||
else if(m_target != nullptr)
|
||||
@@ -3274,20 +3274,18 @@ void monst_basic_abil(short m_num, std::pair<eMonstAbil,uAbility> abil, iLiving*
|
||||
case eMonstAbil::DRAIN_SP:
|
||||
if(pc_target != nullptr) {
|
||||
add_string_to_buf(" Drains " + pc_target->name + '.');
|
||||
pc_target->cur_sp *= abil.second.gen.strength;
|
||||
pc_target->cur_sp /= 100;
|
||||
pc_target->cur_sp = percent(pc_target->cur_sp, abil.second.gen.strength);
|
||||
} else {
|
||||
m_target->spell_note(11);
|
||||
// TODO: If mp < 4 it used to set monster's skill to 1. Should that be restored?
|
||||
m_target->mp *= abil.second.gen.strength;
|
||||
m_target->mp /= 100;
|
||||
m_target->mp = percent(m_target->mp, abil.second.gen.strength);
|
||||
}
|
||||
break;
|
||||
case eMonstAbil::DRAIN_XP:
|
||||
if(pc_target != nullptr) {
|
||||
i = univ.get_target_i(*target);
|
||||
if(pc_target->has_abil_equip(eItemAbil::LIFE_SAVING)) break;
|
||||
drain_pc(*pc_target, univ.town.monst[m_num].level * abil.second.gen.strength / 100);
|
||||
drain_pc(*pc_target, percent(univ.town.monst[m_num].level, abil.second.gen.strength));
|
||||
}
|
||||
break;
|
||||
case eMonstAbil::KILL:
|
||||
|
@@ -1429,8 +1429,7 @@ short damage_monst(cCreature& victim, short who_hit, short how_much, eDamageType
|
||||
}
|
||||
|
||||
if(dam_type < eDamageType::SPECIAL) {
|
||||
how_much *= victim.resist[dam_type];
|
||||
how_much /= 100;
|
||||
how_much = percent(how_much, victim.resist[dam_type]);
|
||||
}
|
||||
|
||||
// Absorb damage?
|
||||
|
@@ -43,6 +43,11 @@ short minmax(short min,short max,short k){
|
||||
return k;
|
||||
}
|
||||
|
||||
int percent(int value, int percentage) {
|
||||
// TODO: Any need to protect from overflow?
|
||||
return (value * percentage) / 100;
|
||||
}
|
||||
|
||||
short gcd(short a, short b){ // Grabbed from Wikipedia and translated to C code
|
||||
short t;
|
||||
while(b != 0){
|
||||
|
@@ -25,6 +25,7 @@ short get_ran(short times, short min, short max);
|
||||
short max(short a,short b);
|
||||
short min(short a,short b);
|
||||
short minmax(short min,short max,short k);
|
||||
int percent(int value, int percentage);
|
||||
short gcd(short a, short b);
|
||||
sf::Time time_in_ticks(int ticks);
|
||||
|
||||
|
@@ -128,8 +128,7 @@ void cCreature::drain_sp(int drain, bool allow_resist) {
|
||||
|
||||
void cCreature::poison(int how_much) {
|
||||
if(how_much != 0) {
|
||||
how_much *= resist[eDamageType::POISON];
|
||||
how_much /= 100;
|
||||
how_much = percent(how_much, resist[eDamageType::POISON]);
|
||||
}
|
||||
apply_status(eStatus::POISON, how_much);
|
||||
if(how_much >= 0)
|
||||
@@ -294,8 +293,7 @@ bool cCreature::is_shielded() const {
|
||||
|
||||
int cCreature::get_shared_dmg(int base_dmg) const {
|
||||
if(abil[eMonstAbil::MARTYRS_SHIELD].active) {
|
||||
base_dmg *= abil[eMonstAbil::MARTYRS_SHIELD].special.extra2;
|
||||
base_dmg /= 100;
|
||||
base_dmg = percent(base_dmg, abil[eMonstAbil::MARTYRS_SHIELD].special.extra2);
|
||||
}
|
||||
return base_dmg;
|
||||
}
|
||||
@@ -310,8 +308,7 @@ int cCreature::magic_adjust(int how_much) {
|
||||
return 0;
|
||||
}
|
||||
// TODO: Magic resistance status effect?
|
||||
how_much *= resist[eDamageType::MAGIC];
|
||||
how_much /= 100;
|
||||
how_much = percent(how_much, resist[eDamageType::MAGIC]);
|
||||
return how_much;
|
||||
}
|
||||
|
||||
|
@@ -61,7 +61,6 @@ void cPlayer::import_legacy(legacy::pc_record_type old){
|
||||
}
|
||||
|
||||
short cPlayer::get_tnl(){
|
||||
short tnl = 100,store_per = 100;
|
||||
// Omitting a race from this list gives it a value of 0, thanks to the defaulting implementation of operator[]
|
||||
static std::map<const eRace, const int> rp = {{eRace::NEPHIL,12},{eRace::SLITH,20},{eRace::VAHNATAI,18}};
|
||||
static std::map<const eTrait, const short> ap = {
|
||||
@@ -71,14 +70,14 @@ short cPlayer::get_tnl(){
|
||||
{eTrait::PACIFIST,-40}, {eTrait::ANAMA,-10},
|
||||
};
|
||||
|
||||
tnl = (tnl * (100 + rp[race])) / 100;
|
||||
short tnl = 100 + rp[race], store_per = 100;
|
||||
for(int i = 0; i < 17; i++) {
|
||||
eTrait trait = eTrait(i);
|
||||
if(traits[trait])
|
||||
store_per = store_per + ap[trait];
|
||||
store_per += ap[trait];
|
||||
}
|
||||
|
||||
tnl = (tnl * store_per) / 100;
|
||||
tnl = percent(tnl, store_per);
|
||||
|
||||
return max(tnl, 10);
|
||||
}
|
||||
|
Reference in New Issue
Block a user