mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-29 23:07:48 +02:00
Fix possible overflow errors on leveling up beyond int64_t limit
- added separate giveExperience method instead of weird changePrimSkill - experience is now always used in form of int64_t - max supported level reduced from 201 to 197 to fit into int64_t - fixed undefined behavior in experience calculation
This commit is contained in:
@@ -365,52 +365,60 @@ void CGameHandler::expGiven(const CGHeroInstance *hero)
|
||||
// levelUpHero(hero);
|
||||
}
|
||||
|
||||
void CGameHandler::changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs)
|
||||
void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountToGain)
|
||||
{
|
||||
if (which == PrimarySkill::EXPERIENCE) // Check if scenario limit reached
|
||||
{
|
||||
if (gs->map->levelLimit != 0)
|
||||
{
|
||||
TExpType expLimit = VLC->heroh->reqExp(gs->map->levelLimit);
|
||||
TExpType resultingExp = abs ? val : hero->exp + val;
|
||||
if (resultingExp > expLimit)
|
||||
{
|
||||
// set given experience to max possible, but don't decrease if hero already over top
|
||||
abs = true;
|
||||
val = std::max(expLimit, hero->exp);
|
||||
TExpType maxExp = VLC->heroh->reqExp(VLC->heroh->maxSupportedLevel());
|
||||
TExpType currExp = hero->exp;
|
||||
|
||||
InfoWindow iw;
|
||||
iw.player = hero->tempOwner;
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 1); //can gain no more XP
|
||||
iw.text.replaceRawString(hero->getNameTranslated());
|
||||
sendAndApply(&iw);
|
||||
}
|
||||
}
|
||||
if (gs->map->levelLimit != 0)
|
||||
maxExp = VLC->heroh->reqExp(gs->map->levelLimit);
|
||||
|
||||
TExpType canGainExp = 0;
|
||||
if (maxExp > currExp)
|
||||
canGainExp = maxExp - currExp;
|
||||
|
||||
if (amountToGain > canGainExp)
|
||||
{
|
||||
// set given experience to max possible, but don't decrease if hero already over top
|
||||
amountToGain = canGainExp;
|
||||
|
||||
InfoWindow iw;
|
||||
iw.player = hero->tempOwner;
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 1); //can gain no more XP
|
||||
iw.text.replaceRawString(hero->getNameTranslated());
|
||||
sendAndApply(&iw);
|
||||
}
|
||||
|
||||
SetPrimSkill sps;
|
||||
sps.id = hero->id;
|
||||
sps.which = PrimarySkill::EXPERIENCE;
|
||||
sps.abs = false;
|
||||
sps.val = amountToGain;
|
||||
sendAndApply(&sps);
|
||||
|
||||
//hero may level up
|
||||
if (hero->commander && hero->commander->alive)
|
||||
{
|
||||
//FIXME: trim experience according to map limit?
|
||||
SetCommanderProperty scp;
|
||||
scp.heroid = hero->id;
|
||||
scp.which = SetCommanderProperty::EXPERIENCE;
|
||||
scp.amount = amountToGain;
|
||||
sendAndApply (&scp);
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
expGiven(hero);
|
||||
}
|
||||
|
||||
void CGameHandler::changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs)
|
||||
{
|
||||
SetPrimSkill sps;
|
||||
sps.id = hero->id;
|
||||
sps.which = which;
|
||||
sps.abs = abs;
|
||||
sps.val = val;
|
||||
sendAndApply(&sps);
|
||||
|
||||
//only for exp - hero may level up
|
||||
if (which == PrimarySkill::EXPERIENCE)
|
||||
{
|
||||
if (hero->commander && hero->commander->alive)
|
||||
{
|
||||
//FIXME: trim experience according to map limit?
|
||||
SetCommanderProperty scp;
|
||||
scp.heroid = hero->id;
|
||||
scp.which = SetCommanderProperty::EXPERIENCE;
|
||||
scp.amount = val;
|
||||
sendAndApply (&scp);
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
|
||||
expGiven(hero);
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs)
|
||||
@@ -658,7 +666,7 @@ void CGameHandler::onNewTurn()
|
||||
{
|
||||
if (obj && obj->ID == Obj::PRISON) //give imprisoned hero 0 exp to level him up. easiest to do at this point
|
||||
{
|
||||
changePrimSkill (getHero(obj->id), PrimarySkill::EXPERIENCE, 0);
|
||||
giveExperience(getHero(obj->id), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3708,7 +3716,7 @@ bool CGameHandler::sacrificeCreatures(const IMarket * market, const CGHeroInstan
|
||||
int expSum = 0;
|
||||
auto finish = [this, &hero, &expSum]()
|
||||
{
|
||||
changePrimSkill(hero, PrimarySkill::EXPERIENCE, hero->calculateXp(expSum));
|
||||
giveExperience(hero, hero->calculateXp(expSum));
|
||||
};
|
||||
|
||||
for(int i = 0; i < slot.size(); ++i)
|
||||
@@ -3749,7 +3757,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
|
||||
int expSum = 0;
|
||||
auto finish = [this, &hero, &expSum]()
|
||||
{
|
||||
changePrimSkill(hero, PrimarySkill::EXPERIENCE, hero->calculateXp(expSum));
|
||||
giveExperience(hero, hero->calculateXp(expSum));
|
||||
};
|
||||
|
||||
for(int i = 0; i < slot.size(); ++i)
|
||||
|
||||
Reference in New Issue
Block a user