From 33f951d74289f4a2ec89ba5ab519074e2b3e255b Mon Sep 17 00:00:00 2001 From: Mykhailo Redko Date: Wed, 20 Mar 2024 06:45:01 +0200 Subject: [PATCH] fix(Core/Spells): Fixed work of sobering spells and other improvements for drunk system (#18390) * fix(Core/Spells): Fix sobering spells and possible uint8 overflow/underflow in SPELL_EFFECT_INEBRIATE handler. * fix(Core/Spells): Improvements for SPELL_AURA_MOD_FAKE_INEBRIATE handling --- src/server/game/Entities/Player/Player.cpp | 46 +++++++++++-------- src/server/game/Entities/Player/Player.h | 2 + .../game/Spells/Auras/SpellAuraEffects.cpp | 40 +++------------- src/server/game/Spells/SpellEffects.cpp | 22 ++++++--- 4 files changed, 50 insertions(+), 60 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index d7dbf6817..19fff250e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -957,7 +957,7 @@ void Player::HandleSobering() SetDrunkValue(drunk); } -DrunkenState Player::GetDrunkenstateByValue(uint8 value) +/*static*/ DrunkenState Player::GetDrunkenstateByValue(uint8 value) { if (value >= 90) return DRUNKEN_SMASHED; @@ -970,27 +970,17 @@ DrunkenState Player::GetDrunkenstateByValue(uint8 value) void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/) { - bool isSobering = newDrunkValue < GetDrunkValue(); + newDrunkValue = std::min(newDrunkValue, 100); + if (newDrunkValue == GetDrunkValue()) + return; + uint32 oldDrunkenState = Player::GetDrunkenstateByValue(GetDrunkValue()); - if (newDrunkValue > 100) - newDrunkValue = 100; - - // select drunk percent or total SPELL_AURA_MOD_FAKE_INEBRIATE amount, whichever is higher for visibility updates - int32 drunkPercent = std::max(newDrunkValue, GetTotalAuraModifier(SPELL_AURA_MOD_FAKE_INEBRIATE)); - if (drunkPercent) - { - m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK); - m_invisibilityDetect.SetValue(INVISIBILITY_DRUNK, drunkPercent); - } - else if (!HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE) && !newDrunkValue) - m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK); - uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue); - SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue); - UpdateObjectVisibility(false); - if (!isSobering) - m_drunkTimer = 0; // reset sobering timer + SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, newDrunkValue); + UpdateInvisibilityDrunkDetect(); + + m_drunkTimer = 0; // reset sobering timer if (newDrunkenState == oldDrunkenState) return; @@ -1003,6 +993,24 @@ void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/) SendMessageToSet(data.Write(), true); } +void Player::UpdateInvisibilityDrunkDetect() +{ + // select drunk percent or total SPELL_AURA_MOD_FAKE_INEBRIATE amount, whichever is higher for visibility updates + uint8 drunkValue = GetDrunkValue(); + int32 fakeDrunkValue = GetFakeDrunkValue(); + int32 maxDrunkValue = std::max(drunkValue, fakeDrunkValue); + + if (maxDrunkValue != 0) + { + m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK); + m_invisibilityDetect.SetValue(INVISIBILITY_DRUNK, maxDrunkValue); + } + else + m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK); + + UpdateObjectVisibility(); +} + void Player::setDeathState(DeathState s, bool /*despawn = false*/) { uint32 ressSpellId = 0; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index a25f8698a..70bd05ba9 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2146,6 +2146,8 @@ public: void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0); [[nodiscard]] uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); } + [[nodiscard]] int32 GetFakeDrunkValue() const { return GetInt32Value(PLAYER_FAKE_INEBRIATION); } + void UpdateInvisibilityDrunkDetect(); static DrunkenState GetDrunkenstateByValue(uint8 value); [[nodiscard]] uint32 GetDeathTimer() const { return m_deathTimer; } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e333b98ee..2d0c5c76f 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -364,7 +364,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS] = &AuraEffect::HandleNoImmediateEffect, //301 SPELL_AURA_SCHOOL_HEAL_ABSORB implemented in Unit::CalcHealAbsorb &AuraEffect::HandleNULL, //302 0 spells in 3.3.5 &AuraEffect::HandleNoImmediateEffect, //303 SPELL_AURA_MOD_DAMAGE_DONE_VERSUS_AURASTATE implemented in Unit::SpellDamageBonus, Unit::MeleeDamageBonus - &AuraEffect::HandleAuraModFakeInebriation, //304 SPELL_AURA_MOD_DRUNK + &AuraEffect::HandleAuraModFakeInebriation, //304 SPELL_AURA_MOD_FAKE_INEBRIATE &AuraEffect::HandleAuraModIncreaseSpeed, //305 SPELL_AURA_MOD_MINIMUM_SPEED &AuraEffect::HandleNULL, //306 0 spells in 3.3.5 &AuraEffect::HandleNULL, //307 0 spells in 3.3.5 @@ -6119,40 +6119,12 @@ void AuraEffect::HandleAuraModFakeInebriation(AuraApplication const* aurApp, uin if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK)) return; - Unit* target = aurApp->GetTarget(); + Player* target = aurApp->GetTarget()->ToPlayer(); + if (!target) + return; - if (apply) - { - target->m_invisibilityDetect.AddFlag(INVISIBILITY_DRUNK); - target->m_invisibilityDetect.AddValue(INVISIBILITY_DRUNK, GetAmount()); - - if (target->GetTypeId() == TYPEID_PLAYER) - { - int32 oldval = target->ToPlayer()->GetInt32Value(PLAYER_FAKE_INEBRIATION); - target->ToPlayer()->SetInt32Value(PLAYER_FAKE_INEBRIATION, oldval + GetAmount()); - } - } - else - { - bool removeDetect = !target->HasAuraType(SPELL_AURA_MOD_FAKE_INEBRIATE); - - target->m_invisibilityDetect.AddValue(INVISIBILITY_DRUNK, -GetAmount()); - - if (target->GetTypeId() == TYPEID_PLAYER) - { - int32 oldval = target->ToPlayer()->GetInt32Value(PLAYER_FAKE_INEBRIATION); - target->ToPlayer()->SetInt32Value(PLAYER_FAKE_INEBRIATION, oldval - GetAmount()); - - if (removeDetect) - removeDetect = !target->ToPlayer()->GetDrunkValue(); - } - - if (removeDetect) - target->m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK); - } - - // call functions which may have additional effects after chainging state of unit - target->UpdateObjectVisibility(false); + target->ApplyModInt32Value(PLAYER_FAKE_INEBRIATION, GetAmount(), apply); + target->UpdateInvisibilityDrunkDetect(); } void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 mode, bool apply) const diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 97cbbd4d2..992f3102b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -4474,17 +4474,25 @@ void Spell::EffectInebriate(SpellEffIndex /*effIndex*/) } uint8 currentDrunk = player->GetDrunkValue(); - uint8 drunkMod = damage; - if (currentDrunk + drunkMod > 100) - { + int32 drunkMod = damage; + + if (drunkMod == 0) + return; + + // drunkMod may contain values ​​that are guaranteed to cause uint8 overflow/underflow (examples: 29690, 46874) + // In addition, we would not want currentDrunk to become more than 100. + // So before adding the values, let's check that everything is fine. + if (drunkMod > 0 && drunkMod > static_cast(100 - currentDrunk)) currentDrunk = 100; - if (rand_chance() < 25.0f) - player->CastSpell(player, 67468, false); // Drunken Vomit - } + else if (drunkMod < 0 && drunkMod < static_cast(0 - currentDrunk)) + currentDrunk = 0; else - currentDrunk += drunkMod; + currentDrunk += drunkMod; // Due to previous checks we can be sure that currentDrunk will not go beyond [0-100] range. player->SetDrunkValue(currentDrunk, m_CastItem ? m_CastItem->GetEntry() : 0); + + if (currentDrunk == 100 && roll_chance_i(25)) + player->CastSpell(player, 67468, false); // Drunken Vomit } void Spell::EffectFeedPet(SpellEffIndex effIndex)