diff --git a/src/game/Object/Object.cpp b/src/game/Object/Object.cpp index d5a29d1e..703f4311 100644 --- a/src/game/Object/Object.cpp +++ b/src/game/Object/Object.cpp @@ -346,28 +346,16 @@ void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* u { return; } 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()) - { - if (((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster()) - { IsActivateToQuest = true; } + IsActivateToQuest = ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster(); - updateMask->SetBit(GAMEOBJECT_DYN_FLAGS); - } - } - 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_DYN_FLAGS); + if (updatetype == UPDATETYPE_VALUES) updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS); - } } 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->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 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 (GetTypeId() == TYPEID_UNIT) + if (appendValue & UNIT_NPC_FLAG_TRAINER) { - if (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; } - } + if (!((Creature*)this)->IsTrainerOf(target, false)) + { appendValue &= ~UNIT_NPC_FLAG_TRAINER; } } - *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) - { - // 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) + if (appendValue & UNIT_NPC_FLAG_STABLEMASTER) { - 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; - } + if (target->getClass() != CLASS_HUNTER) + { appendValue &= ~UNIT_NPC_FLAG_STABLEMASTER; } } - else - { *data << uint32(0); } // disable quest object } - else - { *data << m_uint32Values[index]; } // other cases + + *data << uint32(appendValue); } - } - } - else // other objects case (no special index checks) - { - for (uint16 index = 0; index < m_valuesCount; ++index) - { - if (updateMask->GetBit(index)) + // 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]; } + ++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; } } } diff --git a/src/game/Object/UpdateMask.h b/src/game/Object/UpdateMask.h index 25d51a10..cae39a42 100644 --- a/src/game/Object/UpdateMask.h +++ b/src/game/Object/UpdateMask.h @@ -27,6 +27,9 @@ #include "UpdateFields.h" #include "Errors.h" +#ifdef WIN32 +#include +#endif class UpdateMask { @@ -54,6 +57,20 @@ class UpdateMask 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 GetLength() const { return mBlocks << 2; } uint32 GetCount() const { return mCount; } @@ -124,5 +141,18 @@ class UpdateMask uint32 mCount; uint32 mBlocks; 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 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