Browse Source

Use unsigned types for player experience and related values

pull/2585/head
ephphatha 5 years ago committed by Anders Jenbo
parent
commit
8f2e94ae4d
  1. 12
      Source/loadsave.cpp
  2. 20
      Source/objects.cpp
  3. 2
      Source/pack.h
  4. 19
      Source/player.cpp
  5. 7
      Source/player.h
  6. 2
      Source/qol/xpbar.cpp

12
Source/loadsave.cpp

@ -409,9 +409,9 @@ void LoadPlayer(LoadHelper *file, int p)
player._pLevel = file->NextLE<int8_t>();
player._pMaxLvl = file->NextLE<int8_t>();
file->Skip(2); // Alignment
player._pExperience = file->NextLE<int32_t>();
player._pMaxExp = file->NextLE<int32_t>();
player._pNextExper = file->NextLE<int32_t>();
player._pExperience = file->NextLE<uint32_t>();
file->Skip<uint32_t>(); // Skip _pMaxExp - unused
player._pNextExper = file->NextLE<uint32_t>(); // This can be calculated based on pLevel (which in turn could be calculated based on pExperience)
player._pArmorClass = file->NextLE<int8_t>();
player._pMagResist = file->NextLE<int8_t>();
player._pFireResist = file->NextLE<int8_t>();
@ -1081,9 +1081,9 @@ void SavePlayer(SaveHelper *file, int p)
file->WriteLE<int8_t>(player._pLevel);
file->WriteLE<int8_t>(player._pMaxLvl);
file->Skip(2); // Alignment
file->WriteLE<int32_t>(player._pExperience);
file->WriteLE<int32_t>(player._pMaxExp);
file->WriteLE<int32_t>(player._pNextExper);
file->WriteLE<uint32_t>(player._pExperience);
file->Skip<uint32_t>(); // Skip _pMaxExp
file->WriteLE<uint32_t>(player._pNextExper);
file->WriteLE<int8_t>(player._pArmorClass);
file->WriteLE<int8_t>(player._pMagResist);
file->WriteLE<int8_t>(player._pFireResist);

20
Source/objects.cpp

@ -3408,19 +3408,17 @@ bool OperateShrineGlowing(int pnum)
auto &myPlayer = Players[MyPlayerId];
int playerXP = myPlayer._pExperience;
int magicGain = playerXP / 1000;
int xpLoss = 0;
if (playerXP > 5000) {
magicGain = 5;
xpLoss = static_cast<int>(playerXP * 0.95);
}
ModifyPlrMag(MyPlayerId, magicGain);
myPlayer._pExperience = xpLoss;
// Add 0-5 points to Magic (0.1% of the players XP)
ModifyPlrMag(MyPlayerId, std::min(myPlayer._pExperience / 1000, 5U));
// Take 5% of the players experience to offset the bonus, unless they're very low level in which case take all their experience.
if (myPlayer._pExperience > 5000)
myPlayer._pExperience = static_cast<uint32_t>(myPlayer._pExperience * 0.95);
else
myPlayer._pExperience = 0;
if (sgOptions.Gameplay.bExperienceBar) {
if (sgOptions.Gameplay.bExperienceBar)
force_redraw = 255;
}
CheckStats(Players[pnum]);

2
Source/pack.h

@ -46,7 +46,7 @@ struct PkPlayerStruct {
uint8_t pBaseVit;
int8_t pLevel;
uint8_t pStatPts;
int32_t pExperience;
uint32_t pExperience;
int32_t pGold;
int32_t pHPBase;
int32_t pMaxHPBase;

19
Source/player.cpp

@ -92,7 +92,7 @@ int BlockBonuses[enum_size<HeroClass>::value] = {
};
/** Specifies the experience point limit of each level. */
int ExpLvlsTbl[MAXCHARLEVEL] = {
uint32_t ExpLvlsTbl[MAXCHARLEVEL] = {
0,
2000,
4620,
@ -2477,7 +2477,6 @@ void CreatePlayer(int playerId, HeroClass c)
player._pMaxLvl = player._pLevel;
player._pExperience = 0;
player._pMaxExp = player._pExperience;
player._pNextExper = ExpLvlsTbl[1];
player._pArmorClass = 0;
if (player._pClass == HeroClass::Barbarian) {
@ -2647,8 +2646,6 @@ void NextPlrLevel(int pnum)
CalcPlrInv(pnum, true);
}
#define MAXEXP 2000000000
void AddPlrExperience(int pnum, int lvl, int exp)
{
if (pnum != MyPlayerId) {
@ -2665,21 +2662,21 @@ void AddPlrExperience(int pnum, int lvl, int exp)
}
// Adjust xp based on difference in level between player and monster
exp = std::max(static_cast<int>(exp * (1 + (lvl - player._pLevel) / 10.0)), 0);
uint32_t clampedExp = std::max(static_cast<int>(exp * (1 + (lvl - player._pLevel) / 10.0)), 0);
// Prevent power leveling
if (gbIsMultiplayer) {
auto clampedPlayerLevel = clamp(static_cast<int>(player._pLevel), 0, 50);
const uint32_t clampedPlayerLevel = clamp(static_cast<int>(player._pLevel), 0, 50);
// for low level characters experience gain is capped to 1/20 of current levels xp
// for high level characters experience gain is capped to 200 * current level - this is a smaller value than 1/20 of the exp needed for the next level after level 5.
exp = std::min({ exp, /* level 0-5: */ ExpLvlsTbl[clampedPlayerLevel] / 20, /* level 6-50: */ 200 * clampedPlayerLevel });
clampedExp = std::min({ clampedExp, /* level 0-5: */ ExpLvlsTbl[clampedPlayerLevel] / 20U, /* level 6-50: */ 200U * clampedPlayerLevel });
}
player._pExperience += exp;
if (player._pExperience > MAXEXP || player._pExperience < 0) {
player._pExperience = MAXEXP;
}
constexpr uint32_t MaxExperience = 2000000000U;
// Overflow is only possible if a kill grants more than (2^32-1 - MaxExperience) XP in one go, which doesn't happen in normal gameplay
player._pExperience = std::min(player._pExperience + clampedExp, MaxExperience);
if (sgOptions.Gameplay.bExperienceBar) {
force_redraw = 255;

7
Source/player.h

@ -227,9 +227,8 @@ struct PlayerStruct {
int _pManaPer;
int8_t _pLevel;
int8_t _pMaxLvl;
int _pExperience;
int _pMaxExp;
int _pNextExper;
uint32_t _pExperience;
uint32_t _pNextExper;
int8_t _pArmorClass;
int8_t _pMagResist;
int8_t _pFireResist;
@ -603,6 +602,6 @@ extern int StrengthTbl[enum_size<HeroClass>::value];
extern int MagicTbl[enum_size<HeroClass>::value];
extern int DexterityTbl[enum_size<HeroClass>::value];
extern int VitalityTbl[enum_size<HeroClass>::value];
extern int ExpLvlsTbl[MAXCHARLEVEL];
extern uint32_t ExpLvlsTbl[MAXCHARLEVEL];
} // namespace devilution

2
Source/qol/xpbar.cpp

@ -89,7 +89,7 @@ void DrawXPBar(const Surface &out)
return;
}
const int prevXp = ExpLvlsTbl[charLevel - 1];
const uint64_t prevXp = ExpLvlsTbl[charLevel - 1];
if (player._pExperience < prevXp)
return;

Loading…
Cancel
Save