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
This commit is contained in:
@@ -957,7 +957,7 @@ void Player::HandleSobering()
|
|||||||
SetDrunkValue(drunk);
|
SetDrunkValue(drunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
DrunkenState Player::GetDrunkenstateByValue(uint8 value)
|
/*static*/ DrunkenState Player::GetDrunkenstateByValue(uint8 value)
|
||||||
{
|
{
|
||||||
if (value >= 90)
|
if (value >= 90)
|
||||||
return DRUNKEN_SMASHED;
|
return DRUNKEN_SMASHED;
|
||||||
@@ -970,27 +970,17 @@ DrunkenState Player::GetDrunkenstateByValue(uint8 value)
|
|||||||
|
|
||||||
void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
|
void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
|
||||||
{
|
{
|
||||||
bool isSobering = newDrunkValue < GetDrunkValue();
|
newDrunkValue = std::min<uint8>(newDrunkValue, 100);
|
||||||
|
if (newDrunkValue == GetDrunkValue())
|
||||||
|
return;
|
||||||
|
|
||||||
uint32 oldDrunkenState = Player::GetDrunkenstateByValue(GetDrunkValue());
|
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<int32>(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);
|
uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue);
|
||||||
SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue);
|
|
||||||
UpdateObjectVisibility(false);
|
|
||||||
|
|
||||||
if (!isSobering)
|
SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, newDrunkValue);
|
||||||
m_drunkTimer = 0; // reset sobering timer
|
UpdateInvisibilityDrunkDetect();
|
||||||
|
|
||||||
|
m_drunkTimer = 0; // reset sobering timer
|
||||||
|
|
||||||
if (newDrunkenState == oldDrunkenState)
|
if (newDrunkenState == oldDrunkenState)
|
||||||
return;
|
return;
|
||||||
@@ -1003,6 +993,24 @@ void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
|
|||||||
SendMessageToSet(data.Write(), true);
|
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<int32>(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*/)
|
void Player::setDeathState(DeathState s, bool /*despawn = false*/)
|
||||||
{
|
{
|
||||||
uint32 ressSpellId = 0;
|
uint32 ressSpellId = 0;
|
||||||
|
|||||||
@@ -2146,6 +2146,8 @@ public:
|
|||||||
|
|
||||||
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
|
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
|
||||||
[[nodiscard]] uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); }
|
[[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);
|
static DrunkenState GetDrunkenstateByValue(uint8 value);
|
||||||
|
|
||||||
[[nodiscard]] uint32 GetDeathTimer() const { return m_deathTimer; }
|
[[nodiscard]] uint32 GetDeathTimer() const { return m_deathTimer; }
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS] =
|
|||||||
&AuraEffect::HandleNoImmediateEffect, //301 SPELL_AURA_SCHOOL_HEAL_ABSORB implemented in Unit::CalcHealAbsorb
|
&AuraEffect::HandleNoImmediateEffect, //301 SPELL_AURA_SCHOOL_HEAL_ABSORB implemented in Unit::CalcHealAbsorb
|
||||||
&AuraEffect::HandleNULL, //302 0 spells in 3.3.5
|
&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::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::HandleAuraModIncreaseSpeed, //305 SPELL_AURA_MOD_MINIMUM_SPEED
|
||||||
&AuraEffect::HandleNULL, //306 0 spells in 3.3.5
|
&AuraEffect::HandleNULL, //306 0 spells in 3.3.5
|
||||||
&AuraEffect::HandleNULL, //307 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))
|
if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Unit* target = aurApp->GetTarget();
|
Player* target = aurApp->GetTarget()->ToPlayer();
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
if (apply)
|
target->ApplyModInt32Value(PLAYER_FAKE_INEBRIATION, GetAmount(), apply);
|
||||||
{
|
target->UpdateInvisibilityDrunkDetect();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 mode, bool apply) const
|
||||||
|
|||||||
@@ -4474,17 +4474,25 @@ void Spell::EffectInebriate(SpellEffIndex /*effIndex*/)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8 currentDrunk = player->GetDrunkValue();
|
uint8 currentDrunk = player->GetDrunkValue();
|
||||||
uint8 drunkMod = damage;
|
int32 drunkMod = damage;
|
||||||
if (currentDrunk + drunkMod > 100)
|
|
||||||
{
|
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<int32>(100 - currentDrunk))
|
||||||
currentDrunk = 100;
|
currentDrunk = 100;
|
||||||
if (rand_chance() < 25.0f)
|
else if (drunkMod < 0 && drunkMod < static_cast<int32>(0 - currentDrunk))
|
||||||
player->CastSpell(player, 67468, false); // Drunken Vomit
|
currentDrunk = 0;
|
||||||
}
|
|
||||||
else
|
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);
|
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)
|
void Spell::EffectFeedPet(SpellEffIndex effIndex)
|
||||||
|
|||||||
Reference in New Issue
Block a user