diff --git a/Source/missiles.cpp b/Source/missiles.cpp index 7ad185168da..95ee4790e5a 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -368,7 +368,7 @@ bool Plr2PlrMHit(const Player &player, Player &target, int mindam, int maxdam, i int dam; if (mtype == MissileID::BoneSpirit) { - dam = target._pMaxHP / 3; + dam = player.isOnArenaLevel() ? target._pMaxHP / 3 : target._pHitPoints / 3; } else { dam = mindam + GenerateRnd(maxdam - mindam + 1); if (missileData.isArrow() && damageType == DamageType::Physical) @@ -393,8 +393,8 @@ bool Plr2PlrMHit(const Player &player, Player &target, int mindam, int maxdam, i break; case MissileID::Firebolt: // 100% (200% of default) break; - case MissileID::FireWall: // 100% (200% of default) - dam = dam * 3 / 2; // 150% (300% of default) + case MissileID::FireWall: // 150% (300% of default) + dam = dam * 3 / 2; case MissileID::FlameWave: // 200% (400% of default) dam *= 2; break; @@ -418,17 +418,24 @@ bool Plr2PlrMHit(const Player &player, Player &target, int mindam, int maxdam, i bool isOnArena = player.isOnArenaLevel(); int charLevel = player.getCharacterLevel(); + + // Error handling for critical hit calculation to avoid dividing by 0 in case of bad actor. + charLevel = std::clamp(charLevel, 1, static_cast(GetMaximumCharacterLevel())); + bool isSpell = !missileData.isArrow(); - int critChance = (charLevel * 2 + (isSpell ? player._pMagic : player._pDexterity)) / 10; - bool forcehit = false; + int crit = (isSpell ? player._pMagic : player._pDexterity) / (charLevel / 4); + + crit = std::clamp(crit, 0, 50); + + int critper = GenerateRnd(100); // PVP REBALANCE: Crit chance for arrows and spells in arenas. Crits force hit recovery. - if (isOnArena && GenerateRnd(100) < critChance) { + if (isOnArena && critper < crit && mtype != MissileID::BoneSpirit) { dam = isSpell ? dam * 5 / 4 : dam * 3 / 2; // Arrow: +50% damage, Spell: +25% damage - forcehit = true; } - if (resper > 0 && mtype != MissileID::BoneSpirit) { + // PVP REBALANCE: Bone Spirit is unaffected by resistances on arena levels. + if (resper > 0 && (mtype != MissileID::BoneSpirit || !player.isOnArenaLevel())) { dam -= (dam * resper) / 100; if (&player == MyPlayer) NetSendCmdDamage(true, target, dam, damageType); @@ -442,7 +449,7 @@ bool Plr2PlrMHit(const Player &player, Player &target, int mindam, int maxdam, i } else { if (&player == MyPlayer) NetSendCmdDamage(true, target, dam, damageType); - StartPlrHit(target, dam, forcehit); + StartPlrHit(target, dam, false); } return true; diff --git a/Source/player.cpp b/Source/player.cpp index f1ca3c908c6..e1283d49c49 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -316,7 +316,6 @@ int GetSpellSkippedFrames(SpellID spl) case SpellID::TownPortal: return 4; case SpellID::Flash: - return 1; //case SpellID::StoneCurse: // Page 3 case SpellID::Phasing: @@ -336,6 +335,7 @@ int GetSpellSkippedFrames(SpellID spl) case SpellID::Teleport: return 1; case SpellID::Apocalypse: + return 0; case SpellID::BoneSpirit: return 1; case SpellID::BloodStar: @@ -899,15 +899,20 @@ bool PlrHitPlr(Player &attacker, Player &target) bool isOnArena = attacker.isOnArenaLevel(); int charLevel = attacker.getCharacterLevel(); + + // Error handling for critical hit calculation to avoid dividing by 0 in case of bad actor. + charLevel = std::clamp(charLevel, 1, static_cast(GetMaximumCharacterLevel())); + + int crit = isOnArena ? attacker._pStrength / (charLevel / 4) : charLevel; + + crit = std::clamp(crit, 0, 50); + + int critper = GenerateRnd(100); HeroClass charClass = attacker._pClass; - int critChance = isOnArena ? (charLevel * 2 + attacker._pStrength) / 10 : charLevel; - bool forcehit = false; // PVP REBALANCE: New crit chance formula for arenas. Crits always cause hit recovery. - if ((isOnArena || charClass == HeroClass::Warrior || charClass == HeroClass::Barbarian) && GenerateRnd(100) < critChance) { + if ((isOnArena || charClass == HeroClass::Warrior || charClass == HeroClass::Barbarian) && critper < crit) { dam *= 2; - if (isOnArena) - forcehit = true; } int skdam = dam << 6; @@ -926,7 +931,7 @@ bool PlrHitPlr(Player &attacker, Player &target) if (&attacker == MyPlayer) { NetSendCmdDamage(true, target, skdam, DamageType::Physical); } - StartPlrHit(target, skdam, forcehit); + StartPlrHit(target, skdam, false); return true; }