Merge pull request #3 from Olion17/develop21
[Spells] Heartbeat resist; [Core] UpdateMask refactoring
This commit is contained in:
commit
aa620b3a77
@ -346,28 +346,16 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u
|
|||||||
{ return; }
|
{ return; }
|
||||||
|
|
||||||
bool IsActivateToQuest = false;
|
bool IsActivateToQuest = false;
|
||||||
bool IsPerCasterAuraState = false;
|
//bool IsPerCasterAuraState = false;
|
||||||
|
|
||||||
if (updatetype == UPDATETYPE_CREATE_OBJECT || updatetype == UPDATETYPE_CREATE_OBJECT2)
|
// this is called for UPDATETYPE_CREATE_OBJECT, UPDATETYPE_CREATE_OBJECT2, UPDATETYPE_VALUES only
|
||||||
|
if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
|
||||||
{
|
{
|
||||||
if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
|
IsActivateToQuest = ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster();
|
||||||
{
|
|
||||||
if (((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
|
|
||||||
{ IsActivateToQuest = true; }
|
|
||||||
|
|
||||||
updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
|
updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
|
||||||
}
|
if (updatetype == UPDATETYPE_VALUES)
|
||||||
}
|
|
||||||
else // case UPDATETYPE_VALUES
|
|
||||||
{
|
|
||||||
if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
|
|
||||||
{
|
|
||||||
if (((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
|
|
||||||
{ IsActivateToQuest = true; }
|
|
||||||
|
|
||||||
updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
|
|
||||||
updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS);
|
updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MANGOS_ASSERT(updateMask && updateMask->GetCount() == m_valuesCount);
|
MANGOS_ASSERT(updateMask && updateMask->GetCount() == m_valuesCount);
|
||||||
@ -375,138 +363,150 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u
|
|||||||
*data << (uint8)updateMask->GetBlockCount();
|
*data << (uint8)updateMask->GetBlockCount();
|
||||||
data->append(updateMask->GetMask(), updateMask->GetLength());
|
data->append(updateMask->GetMask(), updateMask->GetLength());
|
||||||
|
|
||||||
|
// checking the new bit extraction mechanic
|
||||||
|
//#ifdef _DEBUG
|
||||||
|
uint16 ix = 0;
|
||||||
|
for (uint16 i = 0; i < m_valuesCount; i++)
|
||||||
|
{
|
||||||
|
if (updateMask->GetBit(i))
|
||||||
|
{
|
||||||
|
ix = updateMask->GetNextSetIndex(ix);
|
||||||
|
if (i != ix)
|
||||||
|
sLog.outError("ERROR BuildValuesUpdate: new index %u, should be %u for object type %u entry %u", ix, i, GetTypeId(), GetEntry());
|
||||||
|
++ix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
|
|
||||||
// 2 specialized loops for speed optimization in non-unit case
|
// 2 specialized loops for speed optimization in non-unit case
|
||||||
if (isType(TYPEMASK_UNIT)) // unit (creature/player) case
|
if (isType(TYPEMASK_UNIT)) // unit (creature/player) case
|
||||||
{
|
{
|
||||||
for (uint16 index = 0; index < m_valuesCount; ++index)
|
uint16 index = 0;
|
||||||
|
while ((index = updateMask->GetNextSetIndex(index)) < m_valuesCount)
|
||||||
{
|
{
|
||||||
if (updateMask->GetBit(index))
|
if (index == UNIT_NPC_FLAGS)
|
||||||
{
|
{
|
||||||
if (index == UNIT_NPC_FLAGS)
|
uint32 appendValue = m_uint32Values[index];
|
||||||
|
|
||||||
|
if (GetTypeId() == TYPEID_UNIT)
|
||||||
{
|
{
|
||||||
uint32 appendValue = m_uint32Values[index];
|
if (appendValue & UNIT_NPC_FLAG_TRAINER)
|
||||||
|
|
||||||
if (GetTypeId() == TYPEID_UNIT)
|
|
||||||
{
|
{
|
||||||
if (appendValue & UNIT_NPC_FLAG_TRAINER)
|
if (!((Creature*)this)->IsTrainerOf(target, false))
|
||||||
{
|
{ appendValue &= ~UNIT_NPC_FLAG_TRAINER; }
|
||||||
if (!((Creature*)this)->IsTrainerOf(target, false))
|
|
||||||
{ appendValue &= ~UNIT_NPC_FLAG_TRAINER; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appendValue & UNIT_NPC_FLAG_STABLEMASTER)
|
|
||||||
{
|
|
||||||
if (target->getClass() != CLASS_HUNTER)
|
|
||||||
{ appendValue &= ~UNIT_NPC_FLAG_STABLEMASTER; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*data << uint32(appendValue);
|
if (appendValue & UNIT_NPC_FLAG_STABLEMASTER)
|
||||||
}
|
|
||||||
// FIXME: Some values at server stored in float format but must be sent to client in uint32 format
|
|
||||||
else if (index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME)
|
|
||||||
{
|
|
||||||
// convert from float to uint32 and send
|
|
||||||
*data << uint32(m_floatValues[index] < 0 ? 0 : m_floatValues[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// there are some float values which may be negative or can't get negative due to other checks
|
|
||||||
else if ((index >= PLAYER_FIELD_NEGSTAT0 && index <= PLAYER_FIELD_NEGSTAT4) ||
|
|
||||||
(index >= PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6)) ||
|
|
||||||
(index >= PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6)) ||
|
|
||||||
(index >= PLAYER_FIELD_POSSTAT0 && index <= PLAYER_FIELD_POSSTAT4))
|
|
||||||
{
|
|
||||||
*data << uint32(m_floatValues[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gamemasters should be always able to select units - remove not selectable flag
|
|
||||||
else if (index == UNIT_FIELD_FLAGS && target->isGameMaster())
|
|
||||||
{
|
|
||||||
*data << (m_uint32Values[index] & ~UNIT_FLAG_NOT_SELECTABLE);
|
|
||||||
}
|
|
||||||
/* Hide loot animation for players that aren't permitted to loot the corpse */
|
|
||||||
else if (index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT)
|
|
||||||
{
|
|
||||||
uint32 send_value = m_uint32Values[index];
|
|
||||||
|
|
||||||
/* Initiate pointer to creature so we can check loot */
|
|
||||||
if (Creature* my_creature = (Creature*)this)
|
|
||||||
/* If the creature is NOT fully looted */
|
|
||||||
if (!my_creature->loot.isLooted())
|
|
||||||
/* If the lootable flag is NOT set */
|
|
||||||
if (!(send_value & UNIT_DYNFLAG_LOOTABLE))
|
|
||||||
{
|
|
||||||
/* Update it on the creature */
|
|
||||||
my_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
|
||||||
/* Update it in the packet */
|
|
||||||
send_value = send_value | UNIT_DYNFLAG_LOOTABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we're not allowed to loot the target, destroy the lootable flag */
|
|
||||||
if (!target->isAllowedToLoot((Creature*)this))
|
|
||||||
if (send_value & UNIT_DYNFLAG_LOOTABLE)
|
|
||||||
{ send_value = send_value & ~UNIT_DYNFLAG_LOOTABLE; }
|
|
||||||
|
|
||||||
/* If we are allowed to loot it and mob is tapped by us, destroy the tapped flag */
|
|
||||||
bool is_tapped = target->IsTappedByMeOrMyGroup((Creature*)this);
|
|
||||||
|
|
||||||
/* If the creature has tapped flag but is tapped by us, remove the flag */
|
|
||||||
if (send_value & UNIT_DYNFLAG_TAPPED && is_tapped)
|
|
||||||
{ send_value = send_value & ~UNIT_DYNFLAG_TAPPED; }
|
|
||||||
|
|
||||||
*data << send_value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// send in current format (float as float, uint32 as uint32)
|
|
||||||
*data << m_uint32Values[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isType(TYPEMASK_GAMEOBJECT)) // gameobject case
|
|
||||||
{
|
|
||||||
for (uint16 index = 0; index < m_valuesCount; ++index)
|
|
||||||
{
|
|
||||||
if (updateMask->GetBit(index))
|
|
||||||
{
|
|
||||||
// send in current format (float as float, uint32 as uint32)
|
|
||||||
if (index == GAMEOBJECT_DYN_FLAGS)
|
|
||||||
{
|
|
||||||
if (IsActivateToQuest)
|
|
||||||
{
|
{
|
||||||
switch (((GameObject*)this)->GetGoType())
|
if (target->getClass() != CLASS_HUNTER)
|
||||||
{
|
{ appendValue &= ~UNIT_NPC_FLAG_STABLEMASTER; }
|
||||||
case GAMEOBJECT_TYPE_QUESTGIVER:
|
|
||||||
case GAMEOBJECT_TYPE_CHEST:
|
|
||||||
case GAMEOBJECT_TYPE_GENERIC:
|
|
||||||
case GAMEOBJECT_TYPE_SPELL_FOCUS:
|
|
||||||
case GAMEOBJECT_TYPE_GOOBER:
|
|
||||||
*data << uint16(GO_DYNFLAG_LO_ACTIVATE);
|
|
||||||
*data << uint16(0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
*data << uint32(0); // unknown, not happen.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{ *data << uint32(0); } // disable quest object
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{ *data << m_uint32Values[index]; } // other cases
|
*data << uint32(appendValue);
|
||||||
}
|
}
|
||||||
}
|
// FIXME: Some values at server stored in float format but must be sent to client in uint32 format
|
||||||
}
|
else if (index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME)
|
||||||
else // other objects case (no special index checks)
|
{
|
||||||
{
|
// convert from float to uint32 and send
|
||||||
for (uint16 index = 0; index < m_valuesCount; ++index)
|
*data << uint32(m_floatValues[index] < 0 ? 0 : m_floatValues[index]);
|
||||||
{
|
}
|
||||||
if (updateMask->GetBit(index))
|
|
||||||
|
// there are some float values which may be negative or can't get negative due to other checks
|
||||||
|
else if ((index >= PLAYER_FIELD_NEGSTAT0 && index <= PLAYER_FIELD_NEGSTAT4) ||
|
||||||
|
(index >= PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6)) ||
|
||||||
|
(index >= PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6)) ||
|
||||||
|
(index >= PLAYER_FIELD_POSSTAT0 && index <= PLAYER_FIELD_POSSTAT4))
|
||||||
|
{
|
||||||
|
*data << uint32(m_floatValues[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gamemasters should be always able to select units - remove not selectable flag
|
||||||
|
else if (index == UNIT_FIELD_FLAGS && target->isGameMaster())
|
||||||
|
{
|
||||||
|
*data << (m_uint32Values[index] & ~UNIT_FLAG_NOT_SELECTABLE);
|
||||||
|
}
|
||||||
|
/* Hide loot animation for players that aren't permitted to loot the corpse */
|
||||||
|
else if (index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT)
|
||||||
|
{
|
||||||
|
uint32 send_value = m_uint32Values[index];
|
||||||
|
|
||||||
|
/* Initiate pointer to creature so we can check loot */
|
||||||
|
if (Creature* my_creature = (Creature*)this)
|
||||||
|
/* If the creature is NOT fully looted */
|
||||||
|
if (!my_creature->loot.isLooted())
|
||||||
|
/* If the lootable flag is NOT set */
|
||||||
|
if (!(send_value & UNIT_DYNFLAG_LOOTABLE))
|
||||||
|
{
|
||||||
|
/* Update it on the creature */
|
||||||
|
my_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
|
||||||
|
/* Update it in the packet */
|
||||||
|
send_value = send_value | UNIT_DYNFLAG_LOOTABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we're not allowed to loot the target, destroy the lootable flag */
|
||||||
|
if (!target->isAllowedToLoot((Creature*)this))
|
||||||
|
if (send_value & UNIT_DYNFLAG_LOOTABLE)
|
||||||
|
{ send_value = send_value & ~UNIT_DYNFLAG_LOOTABLE; }
|
||||||
|
|
||||||
|
/* If we are allowed to loot it and mob is tapped by us, destroy the tapped flag */
|
||||||
|
bool is_tapped = target->IsTappedByMeOrMyGroup((Creature*)this);
|
||||||
|
|
||||||
|
/* If the creature has tapped flag but is tapped by us, remove the flag */
|
||||||
|
if (send_value & UNIT_DYNFLAG_TAPPED && is_tapped)
|
||||||
|
{ send_value = send_value & ~UNIT_DYNFLAG_TAPPED; }
|
||||||
|
|
||||||
|
*data << send_value;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// send in current format (float as float, uint32 as uint32)
|
// send in current format (float as float, uint32 as uint32)
|
||||||
*data << m_uint32Values[index];
|
*data << m_uint32Values[index];
|
||||||
}
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isType(TYPEMASK_GAMEOBJECT)) // gameobject case
|
||||||
|
{
|
||||||
|
uint16 index = 0;
|
||||||
|
while ((index = updateMask->GetNextSetIndex(index)) < m_valuesCount)
|
||||||
|
{
|
||||||
|
// send in current format (float as float, uint32 as uint32)
|
||||||
|
if (index == GAMEOBJECT_DYN_FLAGS)
|
||||||
|
{
|
||||||
|
if (IsActivateToQuest)
|
||||||
|
{
|
||||||
|
switch (((GameObject*)this)->GetGoType())
|
||||||
|
{
|
||||||
|
case GAMEOBJECT_TYPE_QUESTGIVER:
|
||||||
|
case GAMEOBJECT_TYPE_CHEST:
|
||||||
|
case GAMEOBJECT_TYPE_GENERIC:
|
||||||
|
case GAMEOBJECT_TYPE_SPELL_FOCUS:
|
||||||
|
case GAMEOBJECT_TYPE_GOOBER:
|
||||||
|
*data << uint16(GO_DYNFLAG_LO_ACTIVATE);
|
||||||
|
*data << uint16(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*data << uint32(0); // unknown, not happen.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ *data << uint32(0); } // disable quest object
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ *data << m_uint32Values[index]; } // other cases
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // other objects case (no special index checks)
|
||||||
|
{
|
||||||
|
uint16 index = 0;
|
||||||
|
while ((index = updateMask->GetNextSetIndex(index)) < m_valuesCount)
|
||||||
|
{
|
||||||
|
// send in current format (float as float, uint32 as uint32)
|
||||||
|
*data << m_uint32Values[index];
|
||||||
|
++index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,9 @@
|
|||||||
|
|
||||||
#include "UpdateFields.h"
|
#include "UpdateFields.h"
|
||||||
#include "Errors.h"
|
#include "Errors.h"
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
class UpdateMask
|
class UpdateMask
|
||||||
{
|
{
|
||||||
@ -54,6 +57,20 @@ class UpdateMask
|
|||||||
return (((uint8*)mUpdateMask)[ index >> 3 ] & (1 << (index & 0x7))) != 0;
|
return (((uint8*)mUpdateMask)[ index >> 3 ] & (1 << (index & 0x7))) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 GetNextSetIndex(uint32 start) const
|
||||||
|
{
|
||||||
|
uint32 index = start;
|
||||||
|
while (index <= mCount)
|
||||||
|
{
|
||||||
|
uint32 offset = ctz(mUpdateMask[index >> 5] >> (index & 0x1F));
|
||||||
|
if (offset < (32 - (index & 0x1F)))
|
||||||
|
return index + offset;
|
||||||
|
else
|
||||||
|
index += (32 - (index & 0x1F));
|
||||||
|
}
|
||||||
|
return mCount;
|
||||||
|
}
|
||||||
|
|
||||||
uint32 GetBlockCount() const { return mBlocks; }
|
uint32 GetBlockCount() const { return mBlocks; }
|
||||||
uint32 GetLength() const { return mBlocks << 2; }
|
uint32 GetLength() const { return mBlocks << 2; }
|
||||||
uint32 GetCount() const { return mCount; }
|
uint32 GetCount() const { return mCount; }
|
||||||
@ -124,5 +141,18 @@ class UpdateMask
|
|||||||
uint32 mCount;
|
uint32 mCount;
|
||||||
uint32 mBlocks;
|
uint32 mBlocks;
|
||||||
uint32* mUpdateMask;
|
uint32* mUpdateMask;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static uint32 __inline __builtin_ctz(uint32 x)
|
||||||
|
{
|
||||||
|
unsigned long r = 0;
|
||||||
|
_BitScanForward(&r, x);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
static inline uint32 ctz(const uint32 x)
|
||||||
|
{
|
||||||
|
return x ? __builtin_ctz(x) : 32;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@ -4861,6 +4861,13 @@ void Aura::HandleInterruptRegen(bool apply, bool Real)
|
|||||||
GetTarget()->SetInDummyCombatState(apply);
|
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) :
|
SpellAuraHolder::SpellAuraHolder(SpellEntry const* spellproto, Unit* target, WorldObject* caster, Item* castItem) :
|
||||||
m_spellProto(spellproto),
|
m_spellProto(spellproto),
|
||||||
m_target(target), m_castItemGuid(castItem ? castItem->GetObjectGuid() : ObjectGuid()),
|
m_target(target), m_castItemGuid(castItem ? castItem->GetObjectGuid() : ObjectGuid()),
|
||||||
@ -4917,6 +4924,9 @@ SpellAuraHolder::SpellAuraHolder(SpellEntry const* spellproto, Unit* target, Wor
|
|||||||
break;
|
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)
|
for (int32 i = 0; i < MAX_EFFECT_INDEX; ++i)
|
||||||
{ m_auras[i] = NULL; }
|
{ m_auras[i] = NULL; }
|
||||||
}
|
}
|
||||||
@ -5436,6 +5446,18 @@ void SpellAuraHolder::Update(uint32 diff)
|
|||||||
if (Aura* aura = m_auras[i])
|
if (Aura* aura = m_auras[i])
|
||||||
{ aura->UpdateAura(diff); }
|
{ 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
|
// Channeled aura required check distance from caster
|
||||||
if (IsChanneledSpell(m_spellProto) && GetCasterGuid() != m_target->GetObjectGuid())
|
if (IsChanneledSpell(m_spellProto) && GetCasterGuid() != m_target->GetObjectGuid())
|
||||||
{
|
{
|
||||||
@ -5616,6 +5638,14 @@ void SpellAuraHolder::UpdateAuraDuration()
|
|||||||
{ SendAuraDurationForCaster((Player*)caster); }
|
{ 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)
|
void SpellAuraHolder::SendAuraDurationForCaster(Player* caster)
|
||||||
{
|
{
|
||||||
// [-ZERO] Feature doesn't exist in 1.x.
|
// [-ZERO] Feature doesn't exist in 1.x.
|
||||||
|
@ -213,6 +213,7 @@ class SpellAuraHolder
|
|||||||
~SpellAuraHolder();
|
~SpellAuraHolder();
|
||||||
private:
|
private:
|
||||||
void UpdateAuraApplication(); // called at charges or stack changes
|
void UpdateAuraApplication(); // called at charges or stack changes
|
||||||
|
bool HeartbeatResist(uint32 diff);
|
||||||
|
|
||||||
SpellEntry const* m_spellProto;
|
SpellEntry const* m_spellProto;
|
||||||
|
|
||||||
@ -237,6 +238,7 @@ class SpellAuraHolder
|
|||||||
bool m_isPassive: 1;
|
bool m_isPassive: 1;
|
||||||
bool m_isDeathPersist: 1;
|
bool m_isDeathPersist: 1;
|
||||||
bool m_isRemovedOnShapeLost: 1;
|
bool m_isRemovedOnShapeLost: 1;
|
||||||
|
bool m_isHeartbeatSubject: 1;
|
||||||
bool m_deleted: 1;
|
bool m_deleted: 1;
|
||||||
|
|
||||||
uint32 m_in_use; // > 0 while in SpellAuraHolder::ApplyModifiers call/SpellAuraHolder::Update/etc
|
uint32 m_in_use; // > 0 while in SpellAuraHolder::ApplyModifiers call/SpellAuraHolder::Update/etc
|
||||||
|
Loading…
x
Reference in New Issue
Block a user