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:
2023-01-21 17:41:33 -05:00
parent 7b4df6edf8
commit 4c6296612d
7 changed files with 18 additions and 19 deletions

View File

@@ -2736,7 +2736,7 @@ static void run_waterfalls(short mode){ // mode 0 - town, 1 - outdoors
add_string_to_buf(" (No supplies lost.)"); add_string_to_buf(" (No supplies lost.)");
else { else {
cTerrain& ter = univ.scenario.ter_types[coord_to_ter(x, y)]; 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) { if(lost >= ter.flag3) {
lost = ter.flag3; lost = ter.flag3;
add_string_to_buf(" (Many supplies lost.)"); add_string_to_buf(" (Many supplies lost.)");

View File

@@ -3265,7 +3265,7 @@ void monst_basic_abil(short m_num, std::pair<eMonstAbil,uAbility> abil, iLiving*
} }
break; break;
case eMonstAbil::PETRIFY: 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) if(pc_target != nullptr)
petrify_pc(*pc_target, i); petrify_pc(*pc_target, i);
else if(m_target != nullptr) 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: case eMonstAbil::DRAIN_SP:
if(pc_target != nullptr) { if(pc_target != nullptr) {
add_string_to_buf(" Drains " + pc_target->name + '.'); add_string_to_buf(" Drains " + pc_target->name + '.');
pc_target->cur_sp *= abil.second.gen.strength; pc_target->cur_sp = percent(pc_target->cur_sp, abil.second.gen.strength);
pc_target->cur_sp /= 100;
} else { } else {
m_target->spell_note(11); m_target->spell_note(11);
// TODO: If mp < 4 it used to set monster's skill to 1. Should that be restored? // 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 = percent(m_target->mp, abil.second.gen.strength);
m_target->mp /= 100;
} }
break; break;
case eMonstAbil::DRAIN_XP: case eMonstAbil::DRAIN_XP:
if(pc_target != nullptr) { if(pc_target != nullptr) {
i = univ.get_target_i(*target); i = univ.get_target_i(*target);
if(pc_target->has_abil_equip(eItemAbil::LIFE_SAVING)) break; 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; break;
case eMonstAbil::KILL: case eMonstAbil::KILL:

View File

@@ -1429,8 +1429,7 @@ short damage_monst(cCreature& victim, short who_hit, short how_much, eDamageType
} }
if(dam_type < eDamageType::SPECIAL) { if(dam_type < eDamageType::SPECIAL) {
how_much *= victim.resist[dam_type]; how_much = percent(how_much, victim.resist[dam_type]);
how_much /= 100;
} }
// Absorb damage? // Absorb damage?

View File

@@ -43,6 +43,11 @@ short minmax(short min,short max,short k){
return 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 gcd(short a, short b){ // Grabbed from Wikipedia and translated to C code
short t; short t;
while(b != 0){ while(b != 0){

View File

@@ -25,6 +25,7 @@ short get_ran(short times, short min, short max);
short max(short a,short b); short max(short a,short b);
short min(short a,short b); short min(short a,short b);
short minmax(short min,short max,short k); short minmax(short min,short max,short k);
int percent(int value, int percentage);
short gcd(short a, short b); short gcd(short a, short b);
sf::Time time_in_ticks(int ticks); sf::Time time_in_ticks(int ticks);

View File

@@ -128,8 +128,7 @@ void cCreature::drain_sp(int drain, bool allow_resist) {
void cCreature::poison(int how_much) { void cCreature::poison(int how_much) {
if(how_much != 0) { if(how_much != 0) {
how_much *= resist[eDamageType::POISON]; how_much = percent(how_much, resist[eDamageType::POISON]);
how_much /= 100;
} }
apply_status(eStatus::POISON, how_much); apply_status(eStatus::POISON, how_much);
if(how_much >= 0) if(how_much >= 0)
@@ -294,8 +293,7 @@ bool cCreature::is_shielded() const {
int cCreature::get_shared_dmg(int base_dmg) const { int cCreature::get_shared_dmg(int base_dmg) const {
if(abil[eMonstAbil::MARTYRS_SHIELD].active) { if(abil[eMonstAbil::MARTYRS_SHIELD].active) {
base_dmg *= abil[eMonstAbil::MARTYRS_SHIELD].special.extra2; base_dmg = percent(base_dmg, abil[eMonstAbil::MARTYRS_SHIELD].special.extra2);
base_dmg /= 100;
} }
return base_dmg; return base_dmg;
} }
@@ -310,8 +308,7 @@ int cCreature::magic_adjust(int how_much) {
return 0; return 0;
} }
// TODO: Magic resistance status effect? // TODO: Magic resistance status effect?
how_much *= resist[eDamageType::MAGIC]; how_much = percent(how_much, resist[eDamageType::MAGIC]);
how_much /= 100;
return how_much; return how_much;
} }

View File

@@ -61,7 +61,6 @@ void cPlayer::import_legacy(legacy::pc_record_type old){
} }
short cPlayer::get_tnl(){ 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[] // 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 eRace, const int> rp = {{eRace::NEPHIL,12},{eRace::SLITH,20},{eRace::VAHNATAI,18}};
static std::map<const eTrait, const short> ap = { static std::map<const eTrait, const short> ap = {
@@ -71,14 +70,14 @@ short cPlayer::get_tnl(){
{eTrait::PACIFIST,-40}, {eTrait::ANAMA,-10}, {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++) { for(int i = 0; i < 17; i++) {
eTrait trait = eTrait(i); eTrait trait = eTrait(i);
if(traits[trait]) 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); return max(tnl, 10);
} }