From 72acf687dfd40d789a92ffd5b0907434b378c18e Mon Sep 17 00:00:00 2001 From: Eric Robinson Date: Wed, 31 Dec 2025 19:15:35 -0500 Subject: [PATCH 1/2] Refactor Mana Shield damage handling --- Source/player.cpp | 78 ++++++++++++++++++++++++++++++++++------------- Source/player.h | 9 +++--- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/Source/player.cpp b/Source/player.cpp index 045070092..cef1f1959 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -1733,10 +1733,43 @@ bool Player::isWalking() const return IsAnyOf(_pmode, PM_WALK_NORTHWARDS, PM_WALK_SOUTHWARDS, PM_WALK_SIDEWAYS); } -int Player::GetManaShieldDamageReduction() +int Player::ApplyManaShieldToDamage(int totalDamage) { - constexpr uint8_t Max = 7; - return 24 - std::min(_pSplLvl[static_cast(SpellID::ManaShield)], Max) * 3; + if (totalDamage <= 0) + return totalDamage; + + if (!pManaShield || HasAnyOf(_pIFlags, ItemSpecialEffect::NoMana)) + return totalDamage; + + const uint8_t slvl = _pSplLvl[static_cast(SpellID::ManaShield)]; + if (slvl == 0) + return totalDamage; + + constexpr uint8_t MaxLvl = 7; + constexpr int Step = 3; + const uint8_t lvl = std::min(slvl, MaxLvl); + const int R = Step * ((MaxLvl + 1) - lvl); + + // Mana needed to fully negate HP damage. + const int manaNeeded = totalDamage - totalDamage / R; + + if (_pMana >= manaNeeded) { + _pMana -= manaNeeded; + _pManaBase -= manaNeeded; + return 0; + } + + // Not enough mana: spend all mana and convert remaining discounted shortfall back to raw damage. + const int manaSpent = _pMana; + _pMana = 0; + _pManaBase = _pMaxManaBase - _pMaxMana; + + int shortfall = manaNeeded - manaSpent; + + // Invert the discount with the same truncation behavior as the existing code + shortfall += shortfall / (R - 1); + + return shortfall; } void Player::RestorePartialLife() @@ -2821,28 +2854,27 @@ void StripTopGold(Player &player) void ApplyPlrDamage(DamageType damageType, Player &player, int dam, int minHP /*= 0*/, int frac /*= 0*/, DeathReason deathReason /*= DeathReason::MonsterOrTrap*/) { int totalDamage = (dam << 6) + frac; + if (&player == MyPlayer && !player.hasNoLife()) { AddFloatingNumber(damageType, player, totalDamage); } - if (totalDamage > 0 && player.pManaShield && HasNoneOf(player._pIFlags, ItemSpecialEffect::NoMana)) { - const uint8_t manaShieldLevel = player._pSplLvl[static_cast(SpellID::ManaShield)]; - if (manaShieldLevel > 0) { - totalDamage += totalDamage / -player.GetManaShieldDamageReduction(); - } - if (&player == MyPlayer) + + const bool shieldEligible = totalDamage > 0 + && player.pManaShield + && HasNoneOf(player._pIFlags, ItemSpecialEffect::NoMana) + && player._pSplLvl[static_cast(SpellID::ManaShield)] > 0; + + const int manaBefore = player._pMana; + + if (shieldEligible) { + totalDamage = player.ApplyManaShieldToDamage(totalDamage); + + if (&player == MyPlayer) { RedrawComponent(PanelDrawComponent::Mana); - if (player._pMana >= totalDamage) { - player._pMana -= totalDamage; - player._pManaBase -= totalDamage; - totalDamage = 0; - } else { - totalDamage -= player._pMana; - if (manaShieldLevel > 0) { - totalDamage += totalDamage / (player.GetManaShieldDamageReduction() - 1); - } - player._pMana = 0; - player._pManaBase = player._pMaxManaBase - player._pMaxMana; - if (&player == MyPlayer) + + // If effective mana hits zero (fixed-point), shield should end. + // Use a transition guard to avoid spamming the cmd. + if ((manaBefore >> 6) > 0 && (player._pMana >> 6) == 0) NetSendCmd(true, CMD_REMSHIELD); } } @@ -2851,16 +2883,20 @@ void ApplyPlrDamage(DamageType damageType, Player &player, int dam, int minHP /* return; RedrawComponent(PanelDrawComponent::Health); + player._pHitPoints -= totalDamage; player._pHPBase -= totalDamage; + if (player._pHitPoints > player._pMaxHP) { player._pHitPoints = player._pMaxHP; player._pHPBase = player._pMaxHPBase; } + const int minHitPoints = minHP << 6; if (player._pHitPoints < minHitPoints) { SetPlayerHitPoints(player, minHitPoints); } + if (player.hasNoLife()) { SyncPlrKill(player, deathReason); } diff --git a/Source/player.h b/Source/player.h index 1758e2207..a2bc7b7be 100644 --- a/Source/player.h +++ b/Source/player.h @@ -624,12 +624,13 @@ public: return blkper; } + // /** - * @brief Return reciprocal of the factor for calculating damage reduction due to Mana Shield. - * - * Valid only for players with Mana Shield spell level greater than zero. + * @brief Applies fractional damage to Mana if Mana Shield is present + * @param totalDamage - full fractional damage value from damage source before reductions + * @return Remaining fractional damage to apply to Player Life */ - int GetManaShieldDamageReduction(); + int ApplyManaShieldToDamage(int totalDamage); /** * @brief Gets the effective spell level for the player, considering item bonuses From 2ff60f7e038e7b61bee14b419541639126c09a86 Mon Sep 17 00:00:00 2001 From: Eric Robinson Date: Wed, 31 Dec 2025 23:10:56 -0500 Subject: [PATCH 2/2] Update player.h --- Source/player.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/player.h b/Source/player.h index a2bc7b7be..6a6c2a502 100644 --- a/Source/player.h +++ b/Source/player.h @@ -624,7 +624,6 @@ public: return blkper; } - // /** * @brief Applies fractional damage to Mana if Mana Shield is present * @param totalDamage - full fractional damage value from damage source before reductions