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.)");
|
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.)");
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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){
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user