diff --git a/src/game/WorldHandlers/SpellAuras.cpp b/src/game/WorldHandlers/SpellAuras.cpp index 1685c6a5..1198f2c1 100644 --- a/src/game/WorldHandlers/SpellAuras.cpp +++ b/src/game/WorldHandlers/SpellAuras.cpp @@ -4861,6 +4861,13 @@ void Aura::HandleInterruptRegen(bool apply, bool Real) GetTarget()->SetInDummyCombatState(apply); } +// NOTE this may be moved to the header file, but no need because it is used locally +#define HEARTBEAT_AURA_MECHANIC_MASK ( \ + (1 << (MECHANIC_CHARM - 1)) | (1 << (MECHANIC_BANISH - 1)) | (1 << (MECHANIC_DISORIENTED - 1)) | \ + (1 << (MECHANIC_POLYMORPH - 1)) | (1 << (MECHANIC_HORROR - 1)) | (1 << (MECHANIC_FEAR - 1)) | \ + (1 << (MECHANIC_SLEEP - 1)) | (1 << (MECHANIC_SAPPED - 1)) | (1 << (MECHANIC_FREEZE - 1)) | \ + (1 << (MECHANIC_ROOT - 1)) | (1 << (MECHANIC_STUN - 1)) | (1 << (MECHANIC_KNOCKOUT - 1))) + SpellAuraHolder::SpellAuraHolder(SpellEntry const* spellproto, Unit* target, WorldObject* caster, Item* castItem) : m_spellProto(spellproto), m_target(target), m_castItemGuid(castItem ? castItem->GetObjectGuid() : ObjectGuid()), @@ -4917,6 +4924,9 @@ SpellAuraHolder::SpellAuraHolder(SpellEntry const* spellproto, Unit* target, Wor break; } + m_isHeartbeatSubject = (GetSpellMechanicMask(m_spellProto, (1 << MAX_EFFECT_INDEX) - 1) & HEARTBEAT_AURA_MECHANIC_MASK) + && caster->GetTypeId() == TYPEID_PLAYER && target->GetTypeId() == TYPEID_PLAYER && !IsChanneledSpell(m_spellProto); + for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i) { m_auras[i] = NULL; } } @@ -5436,6 +5446,18 @@ void SpellAuraHolder::Update(uint32 diff) if (Aura* aura = m_auras[i]) { aura->UpdateAura(diff); } + if (m_isHeartbeatSubject && m_duration) + { + if (HeartbeatResist(diff)) //m_duration, GetCaster()->ToPlayer(), GetTarget()->ToPlayer() all available within AuraHolder instance + { + if (diff >= 1 * IN_MILLISECONDS || urand(1, 1 * IN_MILLISECONDS) < diff) + { + sLog.outError("Heartbeat: spell %u max timed %u, elapsed %u, chance to resist %f", m_spellProto->Id, m_maxDuration, m_maxDuration - m_duration, pow((m_maxDuration - m_duration + diff) / 15.0f, 2) / (IN_MILLISECONDS*IN_MILLISECONDS)); + m_duration = 1; //equivalent to remove by expire + } + } + } + // Channeled aura required check distance from caster if (IsChanneledSpell(m_spellProto) && GetCasterGuid() != m_target->GetObjectGuid()) { @@ -5616,6 +5638,14 @@ void SpellAuraHolder::UpdateAuraDuration() { SendAuraDurationForCaster((Player*)caster); } } +bool SpellAuraHolder::HeartbeatResist(uint32 diff) +{ + // m_maxDuration - m_duration : Elapsed aura time in ms + // 15 sec is an empirical constant + // TODO use target resistances! the simplest formula from valkyrie-wow is used now + return (urand(0, IN_MILLISECONDS*IN_MILLISECONDS) < pow((m_maxDuration - m_duration + diff) / 15.0f, 2)); +} + void SpellAuraHolder::SendAuraDurationForCaster(Player* caster) { // [-ZERO] Feature doesn't exist in 1.x. diff --git a/src/game/WorldHandlers/SpellAuras.h b/src/game/WorldHandlers/SpellAuras.h index 570ab2e7..25aa7897 100644 --- a/src/game/WorldHandlers/SpellAuras.h +++ b/src/game/WorldHandlers/SpellAuras.h @@ -213,6 +213,7 @@ class SpellAuraHolder ~SpellAuraHolder(); private: void UpdateAuraApplication(); // called at charges or stack changes + bool HeartbeatResist(uint32 diff); SpellEntry const* m_spellProto; @@ -237,6 +238,7 @@ class SpellAuraHolder bool m_isPassive: 1; bool m_isDeathPersist: 1; bool m_isRemovedOnShapeLost: 1; + bool m_isHeartbeatSubject: 1; bool m_deleted: 1; uint32 m_in_use; // > 0 while in SpellAuraHolder::ApplyModifiers call/SpellAuraHolder::Update/etc