Core/Auras: implemented PROC_FLAG_HEARTBEAT and moved food/drink emote mechanic into heartbeat handling (#29943)

* also fixed an false assumption that only one of the food emotes can be played. They can in fact be both done at the same time.
This commit is contained in:
Ovahlord
2024-04-27 13:43:50 +02:00
committed by GitHub
parent 072e6ff9cc
commit d60b4e5b19
7 changed files with 39 additions and 33 deletions

View File

@@ -182,7 +182,6 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
m_regenInterruptTimestamp = GameTime::Now();
m_regenTimer = 0;
m_regenTimerCount = 0;
m_foodEmoteTimerCount = 0;
m_weaponChangeTimer = 0;
m_zoneUpdateId = uint32(-1);
@@ -1624,7 +1623,6 @@ bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo
void Player::RegenerateAll()
{
m_regenTimerCount += m_regenTimer;
m_foodEmoteTimerCount += m_regenTimer;
for (Powers power = POWER_MANA; power < MAX_POWERS; power = Powers(power + 1))
if (power != POWER_RUNES)
@@ -1661,35 +1659,6 @@ void Player::RegenerateAll()
}
m_regenTimer = 0;
// Handles the emotes for drinking and eating.
// According to sniffs there is a background timer going on that repeats independed from the time window where the aura applies.
// That's why we dont need to reset the timer on apply. In sniffs I have seen that the first call for the spell visual is totally random, then after
// 5 seconds over and over again which confirms my theory that we have a independed timer.
if (m_foodEmoteTimerCount >= 5000)
{
auto findInterruptibleEffect = [](AuraEffect const* aurEff)
{
return aurEff->GetSpellInfo()->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing);
};
// Food emote comes above drinking emote if we have to decide (mage regen food for example)
AuraEffectList const& ModRegenAuras = GetAuraEffectsByType(SPELL_AURA_MOD_REGEN);
auto itr = std::find_if(ModRegenAuras.cbegin(), ModRegenAuras.cend(), findInterruptibleEffect);
if (itr != ModRegenAuras.end())
{
SendPlaySpellVisualKit(SPELL_VISUAL_KIT_FOOD, 0, 0);
}
else
{
AuraEffectList const& ModPowerRegenAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN);
itr = std::find_if(ModPowerRegenAuras.cbegin(), ModPowerRegenAuras.cend(), findInterruptibleEffect);
if (itr != ModPowerRegenAuras.end())
SendPlaySpellVisualKit(SPELL_VISUAL_KIT_DRINK, 0, 0);
}
m_foodEmoteTimerCount -= 5000;
}
}
void Player::Regenerate(Powers power)

View File

@@ -2884,7 +2884,6 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
GuidList WhisperList;
TimePoint m_regenInterruptTimestamp;
uint32 m_regenTimerCount;
uint32 m_foodEmoteTimerCount;
std::array<float, MAX_POWERS_PER_CLASS> m_powerFraction;
uint32 m_contestedPvPTimer;

View File

@@ -501,6 +501,17 @@ void Unit::Heartbeat()
// SMSG_FLIGHT_SPLINE_SYNC for cyclic splines
SendFlightSplineSyncUpdate();
// Trigger heartbeat procs and generic aura behavior such as food emotes
TriggerAuraHeartbeat();
}
void Unit::TriggerAuraHeartbeat()
{
for (auto const& [_, auraApplication] : m_appliedAuras)
auraApplication->GetBase()->Heartbeat();
Unit::ProcSkillsAndAuras(this, nullptr, PROC_FLAG_HEARTBEAT, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr);
}
bool Unit::haveOffhandWeapon() const

View File

@@ -685,6 +685,7 @@ class TC_GAME_API Unit : public WorldObject
virtual void Update(uint32 time) override;
void Heartbeat() override;
void TriggerAuraHeartbeat();
void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; }
void resetAttackTimer(WeaponAttackType type = BASE_ATTACK);

View File

@@ -2614,6 +2614,28 @@ void UnitAura::AddStaticApplication(Unit* target, uint32 effMask)
_staticApplications[target->GetGUID()] |= effMask;
}
void UnitAura::Heartbeat()
{
Aura::Heartbeat();
// Periodic food and drink emote animation
HandlePeriodicFoodSpellVisualKit();
}
void UnitAura::HandlePeriodicFoodSpellVisualKit()
{
SpellSpecificType specificType = GetSpellInfo()->GetSpellSpecific();
bool food = specificType == SPELL_SPECIFIC_FOOD || specificType == SPELL_SPECIFIC_FOOD_AND_DRINK;
bool drink = specificType == SPELL_SPECIFIC_DRINK || specificType == SPELL_SPECIFIC_FOOD_AND_DRINK;
if (food)
GetUnitOwner()->SendPlaySpellVisualKit(SPELL_VISUAL_KIT_FOOD, 0, 0);
if (drink)
GetUnitOwner()->SendPlaySpellVisualKit(SPELL_VISUAL_KIT_DRINK, 0, 0);
}
DynObjAura::DynObjAura(AuraCreateInfo const& createInfo)
: Aura(createInfo)
{

View File

@@ -257,6 +257,7 @@ class TC_GAME_API Aura
float CalcPPMProcChance(Unit* actor) const;
void SetLastProcAttemptTime(TimePoint lastProcAttemptTime) { m_lastProcAttemptTime = lastProcAttemptTime; }
void SetLastProcSuccessTime(TimePoint lastProcSuccessTime) { m_lastProcSuccessTime = lastProcSuccessTime; }
virtual void Heartbeat() { }
// AuraScript
void LoadScripts();
@@ -383,6 +384,9 @@ class TC_GAME_API UnitAura : public Aura
void AddStaticApplication(Unit* target, uint32 effMask);
void Heartbeat() override;
void HandlePeriodicFoodSpellVisualKit();
private:
DiminishingGroup m_AuraDRGroup; // Diminishing
std::unordered_map<ObjectGuid, uint32> _staticApplications; // non-area auras

View File

@@ -134,7 +134,7 @@ enum ProcFlags : uint32
{
PROC_FLAG_NONE = 0x00000000,
PROC_FLAG_HEARTBEAT = 0x00000001, // 00 Killed by agressor - not sure about this flag
PROC_FLAG_HEARTBEAT = 0x00000001, // 00 Heartbeat
PROC_FLAG_KILL = 0x00000002, // 01 Kill target (in most cases need XP/Honor reward)
PROC_FLAG_DEAL_MELEE_SWING = 0x00000004, // 02 Done melee auto attack