diff options
Diffstat (limited to 'src')
71 files changed, 2608 insertions, 1699 deletions
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 5ed330a6c48..820e828003a 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -40,8 +40,8 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_UPD_LOGON, "UPDATE account SET salt = ?, verifier = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id AS aId, a.session_key_bnet, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, a.client_build, a.locale, a.recruiter, a.os, a.timezone_offset, ba.id AS baId, aa.SecurityLevel, " - "bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate AS is_bnet_banned, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate AS is_banned, r.id " + PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id AS account_id, a.session_key_bnet, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, a.client_build, a.locale, a.recruiter, a.os, a.timezone_offset, ba.id AS bnet_account_id, ba.email as bnet_account_email, aa.SecurityLevel, " + "bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate AS is_bnet_banned, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate AS is_banned, r.id AS recruitId " "FROM account a LEFT JOIN account r ON a.id = r.recruiter LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id " "LEFT JOIN account_access aa ON a.id = aa.AccountID AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 " "WHERE a.username = ? AND LENGTH(a.session_key_bnet) = 64 ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index f009403755c..66ee41117d4 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -257,8 +257,7 @@ void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientati Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - WaypointNode& waypoint = _path.Nodes.emplace_back(id, x, y, z, orientation, waitTime); - waypoint.MoveType = run ? WaypointMoveType::Run : WaypointMoveType::Walk; + _path.Nodes.emplace_back(id, x, y, z, orientation, waitTime, run ? WaypointMoveType::Run : WaypointMoveType::Walk); } void EscortAI::ResetPath() diff --git a/src/server/game/Entities/Account/Account.cpp b/src/server/game/Entities/Account/Account.cpp new file mode 100644 index 00000000000..1de478212b7 --- /dev/null +++ b/src/server/game/Entities/Account/Account.cpp @@ -0,0 +1,86 @@ +/* +* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "Account.h" +#include "Map.h" +#include "Player.h" +#include "StringFormat.h" +#include "WorldSession.h" + +namespace Battlenet +{ +Account::Account(WorldSession* session, ObjectGuid guid, std::string&& name) : m_session(session), m_name(std::move(name)) +{ + _Create(guid); + + m_entityFragments.Add(WowCS::EntityFragment::FHousingStorage_C, false, WowCS::FragmentSerializationTraits<&Account::m_housingStorageData>{}); + + // Default value + SetUpdateFieldValue(m_values.ModifyValue(&Account::m_housingStorageData).ModifyValue(&UF::HousingStorageData::DecorMaxOwnedCount), 5000); +} + +void Account::ClearUpdateMask(bool remove) +{ + m_values.ClearChangesMask(&Account::m_housingStorageData); + BaseEntity::ClearUpdateMask(remove); +} + +std::string Account::GetNameForLocaleIdx(LocaleConstant /*locale*/) const +{ + return m_name; +} + +void Account::BuildUpdate(UpdateDataMapType& data_map) +{ + BuildUpdateChangesMask(); + + if (Player* owner = m_session->GetPlayer()) + BuildFieldsUpdate(owner, data_map); + + ClearUpdateMask(false); +} + +std::string Account::GetDebugInfo() const +{ + return Trinity::StringFormat("{}\nName: {}", BaseEntity::GetDebugInfo(), m_name); +} + +UF::UpdateFieldFlag Account::GetUpdateFieldFlagsFor(Player const* target) const +{ + if (*target->m_playerData->BnetAccount == GetGUID()) + return UF::UpdateFieldFlag::Owner; + + return UF::UpdateFieldFlag::None; +} + +bool Account::AddToObjectUpdate() +{ + if (Player* owner = m_session->GetPlayer(); owner && owner->IsInWorld()) + { + owner->GetMap()->AddUpdateObject(this); + return true; + } + + return false; +} + +void Account::RemoveFromObjectUpdate() +{ + if (Player* owner = m_session->GetPlayer(); owner && owner->IsInWorld()) + owner->GetMap()->RemoveUpdateObject(this); +} +} diff --git a/src/server/game/Entities/Account/Account.h b/src/server/game/Entities/Account/Account.h new file mode 100644 index 00000000000..3e6f1360e98 --- /dev/null +++ b/src/server/game/Entities/Account/Account.h @@ -0,0 +1,54 @@ +/* +* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef TRINITYCORE_ACCOUNT_H +#define TRINITYCORE_ACCOUNT_H + +#include "BaseEntity.h" + +class WorldSession; + +namespace Battlenet +{ +class Account final : public BaseEntity +{ +public: + explicit Account(WorldSession* session, ObjectGuid guid, std::string&& name); + + void ClearUpdateMask(bool remove) override; + + std::string GetNameForLocaleIdx(LocaleConstant locale) const override; + + void BuildUpdate(UpdateDataMapType& data_map) override; + + std::string GetDebugInfo() const override; + + UF::UpdateField<UF::HousingStorageData, int32(WowCS::EntityFragment::FHousingStorage_C), 0> m_housingStorageData; + +protected: + UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const override; + + bool AddToObjectUpdate() override; + void RemoveFromObjectUpdate() override; + +private: + WorldSession* m_session; + std::string m_name; +}; +} + +#endif // TRINITYCORE_ACCOUNT_H diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 234a03aa9d0..5b0612fedc7 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -138,7 +138,7 @@ bool AreaTrigger::Create(AreaTriggerCreatePropertiesId areaTriggerCreateProperti _areaTriggerTemplate = _areaTriggerCreateProperties->Template; - Object::_Create(ObjectGuid::Create<HighGuid::AreaTrigger>(GetMapId(), GetTemplate() ? GetTemplate()->Id.Id : 0, GetMap()->GenerateLowGuid<HighGuid::AreaTrigger>())); + _Create(ObjectGuid::Create<HighGuid::AreaTrigger>(GetMapId(), GetTemplate() ? GetTemplate()->Id.Id : 0, GetMap()->GenerateLowGuid<HighGuid::AreaTrigger>())); if (GetTemplate()) SetEntry(GetTemplate()->Id.Id); @@ -235,7 +235,10 @@ bool AreaTrigger::Create(AreaTriggerCreatePropertiesId areaTriggerCreateProperti } if (target && HasAreaTriggerFlag(AreaTriggerFieldFlags::Attached)) + { m_movementInfo.transport.guid = target->GetGUID(); + m_updateFlag.MovementTransport = true; + } if (!IsStaticSpawn()) UpdatePositionData(); @@ -296,7 +299,7 @@ bool AreaTrigger::Create(AreaTriggerCreatePropertiesId areaTriggerCreateProperti if (caster) caster->_RegisterAreaTrigger(this); - _ai->OnCreate(spell ? spell : nullptr); + _ai->OnCreate(spell); return true; } @@ -1377,26 +1380,8 @@ void AreaTrigger::UpdateSplinePosition(Movement::Spline<float>& spline) if (_reachedDestination) return; - if (GetElapsedTimeForMovement() >= GetTimeToTarget()) - { - _reachedDestination = true; - _lastSplineIndex = int32(spline.last()); - - G3D::Vector3 lastSplinePosition = spline.getPoint(_lastSplineIndex); - GetMap()->AreaTriggerRelocation(this, lastSplinePosition.x, lastSplinePosition.y, lastSplinePosition.z, GetOrientation()); -#ifdef TRINITY_DEBUG - DebugVisualizePosition(); -#endif - - _ai->OnSplineIndexReached(_lastSplineIndex); - _ai->OnDestinationReached(); - return; - } - - float currentTimePercent = float(GetElapsedTimeForMovement()) / float(GetTimeToTarget()); - - if (currentTimePercent <= 0.f) - return; + float currentTimePercent = std::clamp(float(GetElapsedTimeForMovement()) / float(GetTimeToTarget()), 0.0f, 1.0f); + _reachedDestination = currentTimePercent >= 1.0f; if (m_areaTriggerData->MoveCurveId) { @@ -1435,10 +1420,16 @@ void AreaTrigger::UpdateSplinePosition(Movement::Spline<float>& spline) DebugVisualizePosition(); #endif - if (_lastSplineIndex != lastPositionIndex) + if (_lastSplineIndex != lastPositionIndex || _reachedDestination) { _lastSplineIndex = lastPositionIndex; - _ai->OnSplineIndexReached(_lastSplineIndex); + _ai->OnSplineIndexReached(_lastSplineIndex - _spline->first() /*translate to index of the input array used for AreaTrigger::InitSplines*/); + if (_reachedDestination) + { + _ai->OnDestinationReached(); + _spline = nullptr; + SetUpdateFieldValue(m_values.ModifyValue(&AreaTrigger::m_areaTriggerData).ModifyValue(&UF::AreaTriggerData::PathType), int32(AreaTriggerPathType::None)); + } } } diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.h b/src/server/game/Entities/AreaTrigger/AreaTrigger.h index b4638b57c10..d9b2027c8bd 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.h +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.h @@ -196,6 +196,8 @@ class TC_GAME_API AreaTrigger final : public WorldObject, public GridObject<Area bool HasOrbit() const { return m_areaTriggerData->PathData.Is<UF::AreaTriggerOrbit>(); } UF::AreaTriggerOrbit const& GetOrbit() const { return *m_areaTriggerData->PathData.Get<UF::AreaTriggerOrbit>(); } + void SetPathTarget(ObjectGuid pathTarget) { SetUpdateFieldValue(m_values.ModifyValue(&AreaTrigger::m_areaTriggerData).ModifyValue(&UF::AreaTriggerData::OrbitPathTarget), pathTarget); } + bool HasOverridePosition() const; void UpdateShape(); diff --git a/src/server/game/Entities/Conversation/Conversation.cpp b/src/server/game/Entities/Conversation/Conversation.cpp index 7891bd9bd68..1900374b243 100644 --- a/src/server/game/Entities/Conversation/Conversation.cpp +++ b/src/server/game/Entities/Conversation/Conversation.cpp @@ -176,7 +176,7 @@ void Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Relocate(pos); RelocateStationaryPosition(pos); - Object::_Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid)); + _Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid)); PhasingHandler::InheritPhaseShift(this, creator); UpdatePositionData(); diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 95e8fb5ba0a..3f3038c214c 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -65,7 +65,7 @@ void Corpse::RemoveFromWorld() bool Corpse::Create(ObjectGuid::LowType guidlow, Map* map) { - Object::_Create(ObjectGuid::Create<HighGuid::Corpse>(map->GetId(), 0, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Corpse>(map->GetId(), 0, guidlow)); return true; } @@ -82,7 +82,7 @@ bool Corpse::Create(ObjectGuid::LowType guidlow, Player* owner) return false; } - Object::_Create(ObjectGuid::Create<HighGuid::Corpse>(owner->GetMapId(), 0, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Corpse>(owner->GetMapId(), 0, guidlow)); SetObjectScale(1.0f); SetOwnerGUID(owner->GetGUID()); @@ -190,7 +190,7 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) float o = fields[3].GetFloat(); uint32 mapId = fields[4].GetUInt16(); - Object::_Create(ObjectGuid::Create<HighGuid::Corpse>(mapId, 0, guid)); + _Create(ObjectGuid::Create<HighGuid::Corpse>(mapId, 0, guid)); SetObjectScale(1.0f); SetDisplayId(fields[5].GetUInt32()); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 739beed45c6..8da8904b845 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1829,9 +1829,9 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu SetOriginalEntry(entry); if (vehId || cinfo->VehicleId) - Object::_Create(ObjectGuid::Create<HighGuid::Vehicle>(GetMapId(), entry, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Vehicle>(GetMapId(), entry, guidlow)); else - Object::_Create(ObjectGuid::Create<HighGuid::Creature>(GetMapId(), entry, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Creature>(GetMapId(), entry, guidlow)); if (!UpdateEntry(entry, data)) return false; @@ -3297,7 +3297,7 @@ void Creature::SetVendor(NPCFlags flags, bool apply) if (apply) { if (!m_vendorData) - m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld()); + m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld(), WowCS::FragmentSerializationTraits<&Creature::m_vendorData>{}); SetNpcFlag(flags); SetUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(vendorFlags)); @@ -3319,7 +3319,7 @@ void Creature::SetPetitioner(bool apply) if (apply) { if (!m_vendorData) - m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld()); + m_entityFragments.Add(WowCS::EntityFragment::FVendor_C, IsInWorld(), WowCS::FragmentSerializationTraits<&Creature::m_vendorData>{}); SetNpcFlag(UNIT_NPC_FLAG_PETITIONER); SetUpdateFieldFlagValue(m_values.ModifyValue(&Creature::m_vendorData, 0).ModifyValue(&UF::VendorData::Flags), AsUnderlyingType(VendorDataTypeFlags::Petition)); @@ -3868,31 +3868,17 @@ void Creature::BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Pl { m_objectData->WriteCreate(*data, flags, this, target); m_unitData->WriteCreate(*data, flags, this, target); - - if (m_vendorData) - { - if constexpr (WowCS::IsIndirectFragment(WowCS::EntityFragment::FVendor_C)) - *data << uint8(1); // IndirectFragmentActive: FVendor_C - - m_vendorData->WriteCreate(*data, flags, this, target); - } } void Creature::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { - if (m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment::CGObject)) - { - *data << uint32(m_values.GetChangedObjectTypeMask()); - - if (m_values.HasChanged(TYPEID_OBJECT)) - m_objectData->WriteUpdate(*data, flags, this, target); + *data << uint32(m_values.GetChangedObjectTypeMask()); - if (m_values.HasChanged(TYPEID_UNIT)) - m_unitData->WriteUpdate(*data, flags, this, target); - } + if (m_values.HasChanged(TYPEID_OBJECT)) + m_objectData->WriteUpdate(*data, flags, this, target); - if (m_vendorData && m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment::FVendor_C)) - m_vendorData->WriteUpdate(*data, flags, this, target); + if (m_values.HasChanged(TYPEID_UNIT)) + m_unitData->WriteUpdate(*data, flags, this, target); } void Creature::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index 4a86d6c0e91..d59de6ae949 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -92,7 +92,7 @@ bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caste return false; } - WorldObject::_Create(ObjectGuid::Create<HighGuid::DynamicObject>(GetMapId(), spell->Id, guidlow)); + _Create(ObjectGuid::Create<HighGuid::DynamicObject>(GetMapId(), spell->Id, guidlow)); PhasingHandler::InheritPhaseShift(this, caster); UpdatePositionData(); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index d0f8fb33695..00ef5e065c2 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -430,9 +430,9 @@ public: return 1; } - std::vector<uint32> const* GetPauseTimes() const + std::span<uint32 const> GetPauseTimes() const { - return &_stopFrames; + return _stopFrames; } ObjectGuid GetTransportGUID() const override { return _owner.GetGUID(); } @@ -1023,7 +1023,7 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD m_updateFlag.ServerTime = true; } - Object::_Create(guid); + _Create(guid); m_goInfo = goInfo; m_goTemplateAddon = sObjectMgr->GetGameObjectTemplateAddon(entry); @@ -4136,12 +4136,13 @@ void GameObject::ClearUpdateMask(bool remove) Object::ClearUpdateMask(remove); } -std::vector<uint32> const* GameObject::GetPauseTimes() const +std::span<uint32 const> GameObject::GetPauseTimes() const { + std::span<uint32 const> result; if (GameObjectType::Transport const* transport = dynamic_cast<GameObjectType::Transport const*>(m_goTypeImpl.get())) - return transport->GetPauseTimes(); + result = transport->GetPauseTimes(); - return nullptr; + return result; } void GameObject::SetPathProgressForClient(float progress) @@ -4253,6 +4254,8 @@ void GameObject::SetAnimKitId(uint16 animKitId, bool oneshot) else _animKitId = 0; + m_updateFlag.AnimKit = _animKitId != 0; + WorldPackets::GameObject::GameObjectActivateAnimKit activateAnimKit; activateAnimKit.ObjectGUID = GetGUID(); activateAnimKit.AnimKitID = animKitId; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 800ef83a09e..97333c937eb 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -290,7 +290,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void SetGoAnimProgress(uint8 animprogress) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::PercentHealth), animprogress); } static void SetGoArtKit(uint32 artkit, GameObject* go, ObjectGuid::LowType lowguid = UI64LIT(0)); - std::vector<uint32> const* GetPauseTimes() const; + std::span<uint32 const> GetPauseTimes() const; Optional<float> GetPathProgressForClient() const { return m_transportPathProgress; } void SetPathProgressForClient(float progress); @@ -402,8 +402,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> TransportBase* ToTransportBase() { return const_cast<TransportBase*>(const_cast<GameObject const*>(this)->ToTransportBase()); } TransportBase const* ToTransportBase() const; - Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT) return reinterpret_cast<Transport*>(this); else return nullptr; } - Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT) return reinterpret_cast<Transport const*>(this); else return nullptr; } + Transport* ToTransport() { return GetGoType() == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT ? reinterpret_cast<Transport*>(this) : nullptr; } + Transport const* ToTransport() const { return GetGoType() == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT ? reinterpret_cast<Transport const*>(this) : nullptr; } Position const& GetStationaryPosition() const override { return m_stationaryPosition; } void RelocateStationaryPosition(float x, float y, float z, float o) { m_stationaryPosition.Relocate(x, y, z, o); } diff --git a/src/server/game/Entities/Item/Container/Bag.cpp b/src/server/game/Entities/Item/Container/Bag.cpp index 15d5c6795b2..70ae39e471f 100644 --- a/src/server/game/Entities/Item/Container/Bag.cpp +++ b/src/server/game/Entities/Item/Container/Bag.cpp @@ -75,7 +75,7 @@ bool Bag::Create(ObjectGuid::LowType guidlow, uint32 itemid, ItemContext context if (!itemProto || itemProto->GetContainerSlots() > MAX_BAG_SIZE) return false; - Object::_Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); _bonusData.Initialize(itemProto); diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index cb7bdad249b..5bbd1b81edb 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -506,7 +506,7 @@ Item::~Item() = default; bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext context, Player const* owner) { - Object::_Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Item>(guidlow)); SetEntry(itemId); SetObjectScale(1.0f); @@ -922,7 +922,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie // create item before any checks for store correct guid // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB - Object::_Create(ObjectGuid::Create<HighGuid::Item>(guid)); + _Create(ObjectGuid::Create<HighGuid::Item>(guid)); // Set entry, MUST be before proto check SetEntry(entry); @@ -1786,6 +1786,8 @@ bool Item::IsBindedNotWith(Player const* player) const void Item::BuildUpdate(UpdateDataMapType& data_map) { + BuildUpdateChangesMask(); + if (Player* owner = GetOwner()) BuildFieldsUpdate(owner, data_map); ClearUpdateMask(false); diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index bb3bc76436a..874f1a898f1 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -246,8 +246,8 @@ class TC_GAME_API Item : public Object void SaveRefundDataToDB(); void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans); - Bag* ToBag() { if (IsBag()) return reinterpret_cast<Bag*>(this); else return nullptr; } - Bag const* ToBag() const { if (IsBag()) return reinterpret_cast<Bag const*>(this); else return nullptr; } + Bag* ToBag() { return IsBag() ? reinterpret_cast<Bag*>(this) : nullptr; } + Bag const* ToBag() const { return IsBag() ? reinterpret_cast<Bag const*>(this) : nullptr; } AzeriteItem* ToAzeriteItem() { return IsAzeriteItem() ? reinterpret_cast<AzeriteItem*>(this) : nullptr; } AzeriteItem const* ToAzeriteItem() const { return IsAzeriteItem() ? reinterpret_cast<AzeriteItem const*>(this) : nullptr; } AzeriteEmpoweredItem* ToAzeriteEmpoweredItem() { return IsAzeriteEmpoweredItem() ? reinterpret_cast<AzeriteEmpoweredItem*>(this) : nullptr; } diff --git a/src/server/game/Entities/Object/BaseEntity.cpp b/src/server/game/Entities/Object/BaseEntity.cpp new file mode 100644 index 00000000000..316ef1b3201 --- /dev/null +++ b/src/server/game/Entities/Object/BaseEntity.cpp @@ -0,0 +1,673 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "BaseEntity.h" +#include "Conversation.h" +#include "DB2Structure.h" +#include "Errors.h" +#include "GameTime.h" +#include "Log.h" +#include "MovementPackets.h" +#include "Player.h" +#include "SmoothPhasing.h" +#include "Transport.h" +#include "UpdateData.h" +#include "Vehicle.h" + +BaseEntity::BaseEntity() = default; + +BaseEntity::~BaseEntity() +{ + if (IsInWorld()) + { + TC_LOG_FATAL("misc", "BaseEntity::~BaseEntity {} deleted but still in world!!", GetGUID()); + ABORT(); + } + + if (m_objectUpdated) + { + TC_LOG_FATAL("misc", "BaseEntity::~BaseEntity {} deleted but still in update list!!", GetGUID()); + ABORT(); + } +} + +void BaseEntity::AddToWorld() +{ + if (m_inWorld) + return; + + m_inWorld = true; + + // synchronize values mirror with values array (changes will send in updatecreate opcode any way + ASSERT(!m_objectUpdated); + ClearUpdateMask(false); +} + +void BaseEntity::RemoveFromWorld() +{ + if (!m_inWorld) + return; + + m_inWorld = false; + + // if we remove from world then sending changes not required + ClearUpdateMask(true); +} + +void BaseEntity::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const +{ + if (!target) + return; + + uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT; + uint8 objectType = m_objectTypeId; + CreateObjectBits flags = m_updateFlag; + + if (target == this) // building packet for yourself + { + flags.ThisIsYou = true; + flags.ActivePlayer = true; + objectType = TYPEID_ACTIVE_PLAYER; + } + + if (IsWorldObject()) + { + WorldObject const* worldObject = static_cast<WorldObject const*>(this); + if (worldObject->GetSmoothPhasing() && worldObject->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID())) + flags.SmoothPhasing = true; + } + + ByteBuffer& buf = data->GetBuffer(); + buf << uint8(updateType); + buf << GetGUID(); + buf << uint8(objectType); + + BuildMovementUpdate(&buf, flags, target); + + UF::UpdateFieldFlag fieldFlags = GetUpdateFieldFlagsFor(target); + std::size_t sizePos = buf.wpos(); + buf << uint32(0); + buf << uint8(fieldFlags); + BuildEntityFragments(&buf, m_entityFragments.GetIds()); + + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + WowCS::EntityFragment fragmentId = m_entityFragments.Updateable.Ids[i]; + if (WowCS::IsIndirectFragment(fragmentId)) + buf << uint8(1); // IndirectFragmentActive + + m_entityFragments.Updateable.SerializeCreate[i](this, buf, fieldFlags, target); + } + + buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); + + data->AddUpdateBlock(); +} + +void BaseEntity::SendUpdateToPlayer(Player* player) const +{ + // send create update to player + UpdateData upd(player->GetMapId()); + WorldPacket packet; + + if (player->HaveAtClient(this)) + BuildValuesUpdateBlockForPlayer(&upd, player); + else + BuildCreateUpdateBlockForPlayer(&upd, player); + upd.BuildPacket(&packet); + player->SendDirectMessage(&packet); +} + +void BaseEntity::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const +{ + ByteBuffer& buf = PrepareValuesUpdateBuffer(data); + + EnumFlag<UF::UpdateFieldFlag> fieldFlags = GetUpdateFieldFlagsFor(target); + std::size_t sizePos = buf.wpos(); + buf << uint32(0); + buf << uint8(fieldFlags.HasFlag(UF::UpdateFieldFlag::Owner)); + buf << uint8(m_entityFragments.IdsChanged); + if (m_entityFragments.IdsChanged) + { + buf << uint8(WowCS::EntityFragmentSerializationType::Full); + BuildEntityFragments(&buf, m_entityFragments.GetIds()); + } + buf << uint8(m_entityFragments.ContentsChangedMask); + + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + if (!(m_entityFragments.ContentsChangedMask & m_entityFragments.Updateable.Masks[i])) + continue; + + m_entityFragments.Updateable.SerializeUpdate[i](this, buf, fieldFlags, target); + } + + buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); + + data->AddUpdateBlock(); +} + +inline void BaseEntity::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) +{ + data->append(fragments.data(), fragments.size()); + *data << uint8(WowCS::EntityFragment::End); +} + +void BaseEntity::BuildDestroyUpdateBlock(UpdateData* data) const +{ + data->AddDestroyObject(GetGUID()); +} + +void BaseEntity::BuildOutOfRangeUpdateBlock(UpdateData* data) const +{ + data->AddOutOfRangeGUID(GetGUID()); +} + +ByteBuffer& BaseEntity::PrepareValuesUpdateBuffer(UpdateData* data) const +{ + ByteBuffer& buffer = data->GetBuffer(); + buffer << uint8(UPDATETYPE_VALUES); + buffer << GetGUID(); + return buffer; +} + +void BaseEntity::DestroyForPlayer(Player const* target) const +{ + ASSERT(target); + + UpdateData updateData(target->GetMapId()); + BuildDestroyUpdateBlock(&updateData); + WorldPacket packet; + updateData.BuildPacket(&packet); + target->SendDirectMessage(&packet); +} + +void BaseEntity::SendOutOfRangeForPlayer(Player const* target) const +{ + ASSERT(target); + + UpdateData updateData(target->GetMapId()); + BuildOutOfRangeUpdateBlock(&updateData); + WorldPacket packet; + updateData.BuildPacket(&packet); + target->SendDirectMessage(&packet); +} + +void BaseEntity::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const +{ + std::span<uint32 const> PauseTimes; + if (IsGameObject()) + PauseTimes = static_cast<GameObject const*>(this)->GetPauseTimes(); + + data->WriteBit(flags.HasEntityPosition); + data->WriteBit(flags.NoBirthAnim); + data->WriteBit(flags.EnablePortals); + data->WriteBit(flags.PlayHoverAnim); + data->WriteBit(flags.ThisIsYou); + data->WriteBit(flags.MovementUpdate); + data->WriteBit(flags.MovementTransport); + data->WriteBit(flags.Stationary); + data->WriteBit(flags.CombatVictim); + data->WriteBit(flags.ServerTime); + data->WriteBit(flags.Vehicle); + data->WriteBit(flags.AnimKit); + data->WriteBit(flags.Rotation); + data->WriteBit(flags.GameObject); + data->WriteBit(flags.SmoothPhasing); + data->WriteBit(flags.SceneObject); + data->WriteBit(flags.ActivePlayer); + data->WriteBit(flags.Conversation); + data->WriteBit(flags.Room); + data->WriteBit(flags.Decor); + data->WriteBit(flags.MeshObject); + data->FlushBits(); + + if (flags.MovementUpdate) + { + Unit const* unit = static_cast<Unit const*>(this); + bool HasFallDirection = unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING); + bool HasFall = HasFallDirection || unit->m_movementInfo.jump.fallTime != 0; + bool HasSpline = unit->IsSplineEnabled(); + bool HasInertia = unit->m_movementInfo.inertia.has_value(); + bool HasAdvFlying = unit->m_movementInfo.advFlying.has_value(); + bool HasDriveStatus = unit->m_movementInfo.driveStatus.has_value(); + bool HasStandingOnGameObjectGUID = unit->m_movementInfo.standingOnGameObjectGUID.has_value(); + + *data << GetGUID(); // MoverGUID + + *data << uint32(unit->GetUnitMovementFlags()); + *data << uint32(unit->GetExtraUnitMovementFlags()); + *data << uint32(unit->GetExtraUnitMovementFlags2()); + + *data << uint32(unit->m_movementInfo.time); // MoveTime + *data << float(unit->GetPositionX()); + *data << float(unit->GetPositionY()); + *data << float(unit->GetPositionZ()); + *data << float(unit->GetOrientation()); + + *data << float(unit->m_movementInfo.pitch); // Pitch + *data << float(unit->m_movementInfo.stepUpStartElevation); // StepUpStartElevation + + *data << uint32(0); // RemoveForcesIDs.size() + *data << uint32(0); // MoveIndex + + //for (std::size_t i = 0; i < RemoveForcesIDs.size(); ++i) + // *data << ObjectGuid(RemoveForcesIDs); + + data->WriteBit(HasStandingOnGameObjectGUID); // HasStandingOnGameObjectGUID + data->WriteBit(!unit->m_movementInfo.transport.guid.IsEmpty()); // HasTransport + data->WriteBit(HasFall); // HasFall + data->WriteBit(HasSpline); // HasSpline - marks that the unit uses spline movement + data->WriteBit(false); // HeightChangeFailed + data->WriteBit(false); // RemoteTimeValid + data->WriteBit(HasInertia); // HasInertia + data->WriteBit(HasAdvFlying); // HasAdvFlying + data->WriteBit(HasDriveStatus); // HasDriveStatus + data->FlushBits(); + + if (!unit->m_movementInfo.transport.guid.IsEmpty()) + *data << unit->m_movementInfo.transport; + + if (HasStandingOnGameObjectGUID) + *data << *unit->m_movementInfo.standingOnGameObjectGUID; + + if (HasInertia) + { + *data << unit->m_movementInfo.inertia->id; + *data << unit->m_movementInfo.inertia->force.PositionXYZStream(); + *data << uint32(unit->m_movementInfo.inertia->lifetime); + } + + if (HasAdvFlying) + { + *data << float(unit->m_movementInfo.advFlying->forwardVelocity); + *data << float(unit->m_movementInfo.advFlying->upVelocity); + } + + if (HasFall) + { + *data << uint32(unit->m_movementInfo.jump.fallTime); // Time + *data << float(unit->m_movementInfo.jump.zspeed); // JumpVelocity + + if (data->WriteBit(HasFallDirection)) + { + *data << float(unit->m_movementInfo.jump.sinAngle); // Direction + *data << float(unit->m_movementInfo.jump.cosAngle); + *data << float(unit->m_movementInfo.jump.xyspeed); // Speed + } + } + + if (HasDriveStatus) + { + *data << float(unit->m_movementInfo.driveStatus->speed); + *data << float(unit->m_movementInfo.driveStatus->movementAngle); + data->WriteBit(unit->m_movementInfo.driveStatus->accelerating); + data->WriteBit(unit->m_movementInfo.driveStatus->drifting); + data->FlushBits(); + } + + *data << float(unit->GetSpeed(MOVE_WALK)); + *data << float(unit->GetSpeed(MOVE_RUN)); + *data << float(unit->GetSpeed(MOVE_RUN_BACK)); + *data << float(unit->GetSpeed(MOVE_SWIM)); + *data << float(unit->GetSpeed(MOVE_SWIM_BACK)); + *data << float(unit->GetSpeed(MOVE_FLIGHT)); + *data << float(unit->GetSpeed(MOVE_FLIGHT_BACK)); + *data << float(unit->GetSpeed(MOVE_TURN_RATE)); + *data << float(unit->GetSpeed(MOVE_PITCH_RATE)); + + if (MovementForces const* movementForces = unit->GetMovementForces()) + { + *data << uint32(movementForces->GetForces()->size()); + *data << float(movementForces->GetModMagnitude()); // MovementForcesModMagnitude + } + else + { + *data << uint32(0); + *data << float(1.0f); // MovementForcesModMagnitude + } + + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_AIR_FRICTION)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_MAX_VEL)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LIFT_COEFFICIENT)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_DOUBLE_JUMP_VEL_MOD)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_GLIDE_START_MIN_HEIGHT)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_ADD_IMPULSE_MAX_SPEED)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_BANKING_RATE)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_BANKING_RATE)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_DOWN)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_DOWN)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_UP)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_UP)); + *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); + *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_SURFACE_FRICTION)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_OVER_MAX_DECELERATION)); + *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LAUNCH_SPEED_COEFFICIENT)); + + data->WriteBit(HasSpline); + data->FlushBits(); + + if (MovementForces const* movementForces = unit->GetMovementForces()) + for (MovementForce const& force : *movementForces->GetForces()) + WorldPackets::Movement::CommonMovement::WriteMovementForceWithDirection(force, *data, unit); + + if (HasSpline) + WorldPackets::Movement::CommonMovement::WriteCreateObjectSplineDataBlock(*unit->movespline, *data); + } + + *data << uint32(PauseTimes.size()); + + if (flags.Stationary) + { + WorldObject const* self = static_cast<WorldObject const*>(this); + *data << self->GetStationaryPosition().PositionXYZOStream(); + } + + if (flags.CombatVictim) + { + Unit const* unit = static_cast<Unit const*>(this); + *data << unit->GetVictim()->GetGUID(); // CombatVictim + } + + if (flags.ServerTime) + *data << uint32(GameTime::GetGameTimeMS()); + + if (flags.Vehicle) + { + Unit const* unit = static_cast<Unit const*>(this); + *data << uint32(unit->GetVehicleKit()->GetVehicleInfo()->ID); // RecID + *data << float(unit->GetOrientation()); // InitialRawFacing + } + + if (flags.AnimKit) + { + WorldObject const* self = static_cast<WorldObject const*>(this); + *data << uint16(self->GetAIAnimKitId()); // AiID + *data << uint16(self->GetMovementAnimKitId()); // MovementID + *data << uint16(self->GetMeleeAnimKitId()); // MeleeID + } + + if (flags.Rotation) + { + GameObject const* gameObject = static_cast<GameObject const*>(this); + *data << uint64(gameObject->GetPackedLocalRotation()); // Rotation + } + + //if (flags.Room) + // *data << ObjectGuid(HouseGUID); + + //if (flags.Decor) + // *data << ObjectGuid(RoomGUID); + + //if (flags.MeshObject) + //{ + // *data << ObjectGuid(AttachParentGUID); + // *data << TaggedPosition<Position::XYZ>(PositionLocalSpace); + // *data << QuaternionData(RotationLocalSpace); + // *data << float(ScaleLocalSpace); + // *data << uint8(AttachmentFlags); + //} + + if (!PauseTimes.empty()) + data->append(PauseTimes.data(), PauseTimes.size()); + + if (flags.MovementTransport) + { + WorldObject const* self = static_cast<WorldObject const*>(this); + *data << self->m_movementInfo.transport; + } + + if (flags.GameObject) + { + GameObject const* gameObject = static_cast<GameObject const*>(this); + + bool bit8 = false; + bool isTransport = gameObject->GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT; + + *data << uint32(gameObject->GetWorldEffectID()); + + data->WriteBit(bit8); + data->WriteBit(isTransport); + data->WriteBit(gameObject->GetPathProgressForClient().has_value()); + data->FlushBits(); + if (isTransport) + { + Transport const* transport = static_cast<Transport const*>(gameObject); + uint32 period = transport->GetTransportPeriod(); + + *data << uint32((((int64(transport->GetTimer()) - int64(GameTime::GetGameTimeMS())) % period) + period) % period); // TimeOffset + *data << uint32(transport->GetNextStopTimestamp().value_or(0)); + data->WriteBit(transport->GetNextStopTimestamp().has_value()); + data->WriteBit(transport->IsStopped()); + data->WriteBit(false); + data->FlushBits(); + } + + if (bit8) + *data << uint32(0); + + if (gameObject->GetPathProgressForClient()) + *data << float(*gameObject->GetPathProgressForClient()); + } + + if (flags.SmoothPhasing) + { + SmoothPhasingInfo const* smoothPhasingInfo = static_cast<WorldObject const*>(this)->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID()); + ASSERT(smoothPhasingInfo); + + data->WriteBit(smoothPhasingInfo->ReplaceActive); + data->WriteBit(smoothPhasingInfo->StopAnimKits); + data->WriteBit(smoothPhasingInfo->ReplaceObject.has_value()); + data->FlushBits(); + if (smoothPhasingInfo->ReplaceObject) + *data << *smoothPhasingInfo->ReplaceObject; + } + + if (flags.SceneObject) + { + data->WriteBit(false); // HasLocalScriptData + data->WriteBit(false); // HasPetBattleFullUpdate + data->FlushBits(); + + // if (HasLocalScriptData) + // { + // data->WriteBits(Data.length(), 7); + // data->FlushBits(); + // data->WriteString(Data); + // } + + // if (HasPetBattleFullUpdate) + // { + // for (std::size_t i = 0; i < 2; ++i) + // { + // *data << ObjectGuid(Players[i].CharacterID); + // *data << int32(Players[i].TrapAbilityID); + // *data << int32(Players[i].TrapStatus); + // *data << uint16(Players[i].RoundTimeSecs); + // *data << int8(Players[i].FrontPet); + // *data << uint8(Players[i].InputFlags); + + // data->WriteBits(Players[i].Pets.size(), 2); + // data->FlushBits(); + // for (std::size_t j = 0; j < Players[i].Pets.size(); ++j) + // { + // *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID); + // *data << int32(Players[i].Pets[j].SpeciesID); + // *data << int32(Players[i].Pets[j].CreatureID); + // *data << int32(Players[i].Pets[j].DisplayID); + // *data << int16(Players[i].Pets[j].Level); + // *data << int16(Players[i].Pets[j].Xp); + // *data << int32(Players[i].Pets[j].CurHealth); + // *data << int32(Players[i].Pets[j].MaxHealth); + // *data << int32(Players[i].Pets[j].Power); + // *data << int32(Players[i].Pets[j].Speed); + // *data << int32(Players[i].Pets[j].NpcTeamMemberID); + // *data << uint8(Players[i].Pets[j].BreedQuality); + // *data << uint16(Players[i].Pets[j].StatusFlags); + // *data << int8(Players[i].Pets[j].Slot); + + // *data << uint32(Players[i].Pets[j].Abilities.size()); + // *data << uint32(Players[i].Pets[j].Auras.size()); + // *data << uint32(Players[i].Pets[j].States.size()); + // for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k) + // { + // *data << int32(Players[i].Pets[j].Abilities[k].AbilityID); + // *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining); + // *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining); + // *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex); + // *data << uint8(Players[i].Pets[j].Abilities[k].Pboid); + // } + + // for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k) + // { + // *data << int32(Players[i].Pets[j].Auras[k].AbilityID); + // *data << uint32(Players[i].Pets[j].Auras[k].InstanceID); + // *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining); + // *data << int32(Players[i].Pets[j].Auras[k].CurrentRound); + // *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID); + // } + + // for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k) + // { + // *data << uint32(Players[i].Pets[j].States[k].StateID); + // *data << int32(Players[i].Pets[j].States[k].StateValue); + // } + + // data->WriteBits(Players[i].Pets[j].CustomName.length(), 7); + // data->FlushBits(); + // data->WriteString(Players[i].Pets[j].CustomName); + // } + // } + + // for (std::size_t i = 0; i < 3; ++i) + // { + // *data << uint32(Enviros[j].Auras.size()); + // *data << uint32(Enviros[j].States.size()); + // for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j) + // { + // *data << int32(Enviros[j].Auras[j].AbilityID); + // *data << uint32(Enviros[j].Auras[j].InstanceID); + // *data << int32(Enviros[j].Auras[j].RoundsRemaining); + // *data << int32(Enviros[j].Auras[j].CurrentRound); + // *data << uint8(Enviros[j].Auras[j].CasterPBOID); + // } + + // for (std::size_t j = 0; j < Enviros[j].States.size(); ++j) + // { + // *data << uint32(Enviros[i].States[j].StateID); + // *data << int32(Enviros[i].States[j].StateValue); + // } + // } + + // *data << uint16(WaitingForFrontPetsMaxSecs); + // *data << uint16(PvpMaxRoundTime); + // *data << int32(CurRound); + // *data << uint32(NpcCreatureID); + // *data << uint32(NpcDisplayID); + // *data << int8(CurPetBattleState); + // *data << uint8(ForfeitPenalty); + // *data << ObjectGuid(InitialWildPetGUID); + // data->WriteBit(IsPVP); + // data->WriteBit(CanAwardXP); + // data->FlushBits(); + // } + } + + if (flags.ActivePlayer) + { + Player const* player = static_cast<Player const*>(this); + + bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty(); + bool HasRuneState = player->GetPowerIndex(POWER_RUNES) != MAX_POWERS; + + data->WriteBit(HasSceneInstanceIDs); + data->WriteBit(HasRuneState); + data->FlushBits(); + if (HasSceneInstanceIDs) + { + *data << uint32(player->GetSceneMgr().GetSceneTemplateByInstanceMap().size()); + for (auto const& [sceneInstanceId, _] : player->GetSceneMgr().GetSceneTemplateByInstanceMap()) + *data << uint32(sceneInstanceId); + } + if (HasRuneState) + { + float baseCd = float(player->GetRuneBaseCooldown()); + uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES)); + + *data << uint8((1 << maxRunes) - 1); + *data << uint8(player->GetRunesState()); + *data << uint32(maxRunes); + for (uint32 i = 0; i < maxRunes; ++i) + *data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); + } + } + + if (flags.Conversation) + { + Conversation const* self = static_cast<Conversation const*>(this); + if (data->WriteBit(self->GetTextureKitId() != 0)) + *data << uint32(self->GetTextureKitId()); + + data->FlushBits(); + } +} + +UF::UpdateFieldFlag BaseEntity::GetUpdateFieldFlagsFor(Player const* /*target*/) const +{ + return UF::UpdateFieldFlag::None; +} + +void BaseEntity::AddToObjectUpdateIfNeeded() +{ + if (m_inWorld && !m_objectUpdated) + m_objectUpdated = AddToObjectUpdate(); +} + +void BaseEntity::ClearUpdateMask(bool remove) +{ + m_entityFragments.IdsChanged = false; + + if (m_objectUpdated) + { + if (remove) + RemoveFromObjectUpdate(); + m_objectUpdated = false; + } +} + +void BaseEntity::BuildUpdateChangesMask() +{ + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) + { + if (m_entityFragments.Updateable.IsChanged[i](this)) + m_entityFragments.ContentsChangedMask |= m_entityFragments.Updateable.Masks[i]; + else + m_entityFragments.ContentsChangedMask &= ~m_entityFragments.Updateable.Masks[i]; + } +} + +void BaseEntity::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const +{ + UpdateDataMapType::iterator iter = data_map.emplace(player, player->GetMapId()).first; + BuildValuesUpdateBlockForPlayer(&iter->second, iter->first); +} + +std::string BaseEntity::GetDebugInfo() const +{ + return Trinity::StringFormat("{}", GetGUID()); +} diff --git a/src/server/game/Entities/Object/BaseEntity.h b/src/server/game/Entities/Object/BaseEntity.h new file mode 100644 index 00000000000..bed8fa3ae51 --- /dev/null +++ b/src/server/game/Entities/Object/BaseEntity.h @@ -0,0 +1,419 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITYCORE_BASE_ENTITY_H +#define TRINITYCORE_BASE_ENTITY_H + +#include "Common.h" +#include "ObjectGuid.h" +#include "UpdateFields.h" +#include "WowCSEntityDefinitions.h" +#include <unordered_map> + +class BaseEntity; +class Player; +class UpdateData; +class WorldPacket; + +typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType; + +struct CreateObjectBits +{ + bool HasEntityPosition : 1; + bool NoBirthAnim : 1; + bool EnablePortals : 1; + bool PlayHoverAnim : 1; + bool ThisIsYou : 1; + bool MovementUpdate : 1; + bool MovementTransport : 1; + bool Stationary : 1; + bool CombatVictim : 1; + bool ServerTime : 1; + bool Vehicle : 1; + bool AnimKit : 1; + bool Rotation : 1; + bool GameObject : 1; + bool SmoothPhasing : 1; + bool SceneObject : 1; + bool ActivePlayer : 1; + bool Conversation : 1; + bool Room : 1; + bool Decor : 1; + bool MeshObject : 1; + + void Clear() + { + memset(this, 0, sizeof(CreateObjectBits)); + } +}; + +namespace UF +{ + class UpdateFieldHolder + { + public: + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline MutableFieldReference<T, false> ModifyValue(UpdateField<T, BlockBit, Bit>(Derived::* field)); + + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline OptionalUpdateFieldSetter<T> ModifyValue(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field)); + + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline MutableFieldReference<T, false> ModifyValue(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field), uint32 /*dummy*/); + + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline void ClearChangesMask(UpdateField<T, BlockBit, Bit>(Derived::* field)); + + template<typename Derived, typename T, int32 BlockBit, uint32 Bit> + inline void ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field)); + + uint32 GetChangedObjectTypeMask() const { return _changesMask; } + + bool HasChanged(uint32 index) const { return (_changesMask & UpdateMaskHelpers::GetBlockFlag(index)) != 0; } + + inline BaseEntity* GetOwner(); + + private: + friend BaseEntity; + + // This class is tightly tied to BaseEntity::m_values member, do not construct elsewhere + UpdateFieldHolder() = default; + + uint32 _changesMask = 0; // changes mask for data of Object subclasses + }; + + template<typename T> + inline bool SetUpdateFieldValue(UpdateFieldPrivateSetter<T>& setter, typename UpdateFieldPrivateSetter<T>::value_type&& value) + { + return setter.SetValue(std::move(value)); + } + + template<typename T> + inline typename DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter) + { + return setter.AddValue(); + } + + template<typename T> + inline typename DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) + { + return setter.InsertValue(index); + } + + template<typename T> + inline void RemoveDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) + { + setter.RemoveValue(index); + } + + template<typename T> + inline void ClearDynamicUpdateFieldValues(DynamicUpdateFieldSetter<T>& setter) + { + setter.Clear(); + } + + template<typename K, typename V> + inline void RemoveMapUpdateFieldValue(MapUpdateFieldSetter<K, V>& setter, std::type_identity_t<K> const& key) + { + setter.RemoveKey(key); + } + + template<typename T> + inline void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter<T>& setter) + { + setter.RemoveValue(); + } +} + +class TC_GAME_API BaseEntity +{ + ObjectGuid m_guid; + + public: + virtual ~BaseEntity(); + + bool IsInWorld() const { return m_inWorld; } + + virtual void AddToWorld(); + virtual void RemoveFromWorld(); + + ObjectGuid const& GetGUID() const { return m_guid; } + static ObjectGuid GetGUID(BaseEntity const* o) { return o ? o->GetGUID() : ObjectGuid::Empty; } + + TypeID GetTypeId() const { return m_objectTypeId; } + bool isType(TypeMask mask) const { return (ObjectTypeMask[m_objectTypeId] & mask) != 0; } + + inline bool IsWorldObject() const { return isType(TYPEMASK_WORLDOBJECT); } + inline bool IsItem() const { return isType(TYPEMASK_ITEM); } + inline bool IsUnit() const { return isType(TYPEMASK_UNIT); } + inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; } + inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; } + inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; } + inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; } + inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; } + inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; } + inline bool IsSceneObject() const { return GetTypeId() == TYPEID_SCENEOBJECT; } + inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; } + inline bool IsMeshObject() const { return GetTypeId() == TYPEID_MESH_OBJECT; } + + virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const; + void SendUpdateToPlayer(Player* player) const; + + void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const; + void BuildDestroyUpdateBlock(UpdateData* data) const; + void BuildOutOfRangeUpdateBlock(UpdateData* data) const; + ByteBuffer& PrepareValuesUpdateBuffer(UpdateData* data) const; + + virtual void DestroyForPlayer(Player const* target) const; + void SendOutOfRangeForPlayer(Player const* target) const; + + virtual void ClearUpdateMask(bool remove); + + virtual std::string GetNameForLocaleIdx(LocaleConstant locale) const = 0; + + void SetIsNewObject(bool enable) { m_isNewObject = enable; } + bool IsDestroyedObject() const { return m_isDestroyedObject; } + void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; } + virtual void BuildUpdate([[maybe_unused]] UpdateDataMapType& data_map) { } + void BuildUpdateChangesMask(); + void BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const; + + friend UF::UpdateFieldHolder; + UF::UpdateFieldHolder m_values; + + template<typename T> + void ForceUpdateFieldChange(UF::UpdateFieldPrivateSetter<T> const& /*setter*/) + { + AddToObjectUpdateIfNeeded(); + } + + virtual std::string GetDebugInfo() const; + + protected: + BaseEntity(); + + void _Create(ObjectGuid const& guid) { m_guid = guid; } + + template<typename T> + void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) + { + if (UF::SetUpdateFieldValue(setter, std::move(value))) + AddToObjectUpdateIfNeeded(); + } + + template<typename T> + void SetUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) + { + static_assert(std::is_integral_v<T>, "SetUpdateFieldFlagValue must be used with integral types"); + this->SetUpdateFieldValue(setter, setter.GetValue() | flag); + } + + template<typename T> + void RemoveUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) + { + static_assert(std::is_integral_v<T>, "RemoveUpdateFieldFlagValue must be used with integral types"); + this->SetUpdateFieldValue(setter, setter.GetValue() & ~flag); + } + + template<typename T> + typename UF::DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter) + { + AddToObjectUpdateIfNeeded(); + return UF::AddDynamicUpdateFieldValue(setter); + } + + template<typename T> + typename UF::DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) + { + AddToObjectUpdateIfNeeded(); + return UF::InsertDynamicUpdateFieldValue(setter, index); + } + + template<typename T> + void RemoveDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) + { + AddToObjectUpdateIfNeeded(); + UF::RemoveDynamicUpdateFieldValue(setter, index); + } + + template<typename K, typename V> + void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter<K, V> setter, std::type_identity_t<K> const& key) + { + AddToObjectUpdateIfNeeded(); + UF::RemoveMapUpdateFieldValue(setter, key); + } + + template<typename T> + void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter<T> setter) + { + AddToObjectUpdateIfNeeded(); + UF::ClearDynamicUpdateFieldValues(setter); + } + + template<typename T> + void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter<T> setter) + { + AddToObjectUpdateIfNeeded(); + UF::RemoveOptionalUpdateFieldValue(setter); + } + + // stat system helpers + template<typename T> + void SetUpdateFieldStatValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) + { + static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types"); + this->SetUpdateFieldValue(setter, std::max(value, T(0))); + } + + template<typename T> + void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type mod, bool apply) + { + static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types"); + + T value = setter.GetValue(); + if (apply) + value += mod; + else + value -= mod; + + this->SetUpdateFieldValue(setter, value); + } + + template<typename T> + void ApplyPercentModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, float percent, bool apply) + { + static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types"); + + T value = setter.GetValue(); + + // don't want to include Util.h here + //ApplyPercentModFloatVar(value, percent, apply); + if (percent == -100.0f) + percent = -99.99f; + value *= (apply ? (100.0f + percent) / 100.0f : 100.0f / (100.0f + percent)); + + this->SetUpdateFieldValue(setter, value); + } + + template<typename Action> + void DoWithSuppressingObjectUpdates(Action&& action) + { + bool wasUpdatedBeforeAction = m_objectUpdated; + action(); + if (m_objectUpdated && !wasUpdatedBeforeAction) + { + RemoveFromObjectUpdate(); + m_objectUpdated = false; + } + } + + void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const; + virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const; + static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments); + + TypeID m_objectTypeId = NUM_CLIENT_OBJECT_TYPES; + CreateObjectBits m_updateFlag = {}; + WowCS::EntityFragmentsHolder m_entityFragments; + + virtual bool AddToObjectUpdate() = 0; + virtual void RemoveFromObjectUpdate() = 0; + void AddToObjectUpdateIfNeeded(); + + bool m_objectUpdated = false; + + private: + bool m_inWorld = false; + bool m_isNewObject = false; + bool m_isDestroyedObject = false; + + BaseEntity(BaseEntity const& right) = delete; + BaseEntity(BaseEntity&& right) = delete; + BaseEntity& operator=(BaseEntity const& right) = delete; + BaseEntity& operator=(BaseEntity&& right) = delete; +}; + +inline BaseEntity* UF::UpdateFieldHolder::GetOwner() +{ +#if TRINITY_COMPILER == TRINITY_COMPILER_GNU +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + + return reinterpret_cast<BaseEntity*>(reinterpret_cast<std::byte*>(this) - offsetof(BaseEntity, m_values)); + +#if TRINITY_COMPILER == TRINITY_COMPILER_GNU +#pragma GCC diagnostic pop +#endif +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + return { (static_cast<Derived*>(owner)->*field)._value }; +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + return { static_cast<Derived*>(owner)->*field }; +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field, uint32 /*dummy*/) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + auto& uf = (static_cast<Derived*>(owner)->*field); + if (!uf.has_value()) + uf.ConstructValue(); + + return { *uf._value }; +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit> Derived::* field) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); + + (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask(); +} + +template <typename Derived, typename T, int32 BlockBit, uint32 Bit> +inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) +{ + BaseEntity* owner = GetOwner(); + if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); + + auto& uf = (static_cast<Derived*>(owner)->*field); + if (uf.has_value()) + uf._value->ClearChangesMask(); +} + +#endif // TRINITYCORE_BASE_ENTITY_H diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index ac2219ed5cf..a045de03222 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -20,19 +20,16 @@ #include "CellImpl.h" #include "CinematicMgr.h" #include "CombatLogPackets.h" -#include "Common.h" -#include "Creature.h" +#include "Corpse.h" #include "CreatureGroups.h" #include "DB2Stores.h" -#include "GameTime.h" +#include "DynamicObject.h" +#include "GameObject.h" #include "GridNotifiersImpl.h" -#include "G3DPosition.hpp" #include "InstanceScenario.h" -#include "Item.h" #include "Log.h" +#include "Map.h" #include "MiscPackets.h" -#include "MovementPackets.h" -#include "MovementTypedefs.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "OutdoorPvPMgr.h" @@ -41,17 +38,14 @@ #include "Player.h" #include "ReputationMgr.h" #include "SmoothPhasing.h" +#include "Spell.h" #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "SpellPackets.h" #include "TemporarySummon.h" #include "Totem.h" -#include "Transport.h" -#include "Unit.h" #include "UpdateData.h" -#include "Util.h" #include "VMapFactory.h" -#include "Vehicle.h" #include "VMapManager.h" #include "World.h" #include <G3D/Vector3.h> @@ -70,49 +64,16 @@ constexpr float VisibilityDistances[AsUnderlyingType(VisibilityDistanceType::Max Object::Object() : m_scriptRef(this, NoopObjectDeleter()) { m_objectTypeId = TYPEID_OBJECT; - m_updateFlag.Clear(); - m_entityFragments.Add(WowCS::EntityFragment::CGObject, false); - - m_inWorld = false; - m_isNewObject = false; - m_isDestroyedObject = false; - m_objectUpdated = false; -} - -Object::~Object() -{ - if (IsInWorld()) - { - TC_LOG_FATAL("misc", "Object::~Object {} deleted but still in world!!", GetGUID().ToString()); - if (Item* item = ToItem()) - TC_LOG_FATAL("misc", "Item slot {}", item->GetSlot()); - ABORT(); - } - - if (m_objectUpdated) - { - TC_LOG_FATAL("misc", "Object::~Object {} deleted but still in update list!!", GetGUID().ToString()); - ABORT(); - } + m_entityFragments.Add(WowCS::EntityFragment::CGObject, false, + &Object::BuildObjectFragmentCreate, &Object::BuildObjectFragmentUpdate, &Object::IsObjectFragmentChanged); } -void Object::_Create(ObjectGuid const& guid) -{ - m_objectUpdated = false; - m_guid = guid; -} +Object::~Object() = default; void Object::AddToWorld() { - if (m_inWorld) - return; - - m_inWorld = true; - - // synchronize values mirror with values array (changes will send in updatecreate opcode any way - ASSERT(!m_objectUpdated); - ClearUpdateMask(false); + BaseEntity::AddToWorld(); // Set new ref when adding to world (except if we already have one - also set in constructor to allow scripts to work in initialization phase) // Changing the ref when adding/removing from world prevents accessing players on different maps (possibly from another thread) @@ -122,109 +83,11 @@ void Object::AddToWorld() void Object::RemoveFromWorld() { - if (!m_inWorld) - return; - - m_inWorld = false; - - // if we remove from world then sending changes not required - ClearUpdateMask(true); + BaseEntity::RemoveFromWorld(); m_scriptRef = nullptr; } -void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const -{ - if (!target) - return; - - uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT; - uint8 objectType = m_objectTypeId; - CreateObjectBits flags = m_updateFlag; - - if (target == this) // building packet for yourself - { - flags.ThisIsYou = true; - flags.ActivePlayer = true; - objectType = TYPEID_ACTIVE_PLAYER; - } - - if (IsWorldObject()) - { - WorldObject const* worldObject = static_cast<WorldObject const*>(this); - if (!flags.MovementUpdate && !worldObject->m_movementInfo.transport.guid.IsEmpty()) - flags.MovementTransport = true; - - if (worldObject->GetAIAnimKitId() || worldObject->GetMovementAnimKitId() || worldObject->GetMeleeAnimKitId()) - flags.AnimKit = true; - - if (worldObject->GetSmoothPhasing() && worldObject->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID())) - flags.SmoothPhasing = true; - } - - if (Unit const* unit = ToUnit()) - { - flags.PlayHoverAnim = unit->IsPlayingHoverAnim(); - - if (unit->GetVictim()) - flags.CombatVictim = true; - } - - ByteBuffer& buf = data->GetBuffer(); - buf << uint8(updateType); - buf << GetGUID(); - buf << uint8(objectType); - - BuildMovementUpdate(&buf, flags, target); - - UF::UpdateFieldFlag fieldFlags = GetUpdateFieldFlagsFor(target); - std::size_t sizePos = buf.wpos(); - buf << uint32(0); - buf << uint8(fieldFlags); - BuildEntityFragments(&buf, m_entityFragments.GetIds()); - buf << uint8(1); // IndirectFragmentActive: CGObject - BuildValuesCreate(&buf, fieldFlags, target); - buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); - - data->AddUpdateBlock(); -} - -void Object::SendUpdateToPlayer(Player* player) -{ - // send create update to player - UpdateData upd(player->GetMapId()); - WorldPacket packet; - - if (player->HaveAtClient(this)) - BuildValuesUpdateBlockForPlayer(&upd, player); - else - BuildCreateUpdateBlockForPlayer(&upd, player); - upd.BuildPacket(&packet); - player->SendDirectMessage(&packet); -} - -void Object::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const -{ - ByteBuffer& buf = PrepareValuesUpdateBuffer(data); - - EnumFlag<UF::UpdateFieldFlag> fieldFlags = GetUpdateFieldFlagsFor(target); - std::size_t sizePos = buf.wpos(); - buf << uint32(0); - buf << uint8(fieldFlags.HasFlag(UF::UpdateFieldFlag::Owner)); - buf << uint8(m_entityFragments.IdsChanged); - if (m_entityFragments.IdsChanged) - { - buf << uint8(WowCS::EntityFragmentSerializationType::Full); - BuildEntityFragments(&buf, m_entityFragments.GetIds()); - } - buf << uint8(m_entityFragments.ContentsChangedMask); - - BuildValuesUpdate(&buf, fieldFlags, target); - buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4); - - data->AddUpdateBlock(); -} - void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::UpdateFieldFlag flags, Player const* target) const { ByteBuffer& buf = PrepareValuesUpdateBuffer(data); @@ -238,486 +101,21 @@ void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::Updat data->AddUpdateBlock(); } -void Object::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments) -{ - data->append(fragments.data(), fragments.size()); - *data << uint8(WowCS::EntityFragment::End); -} - void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const { - uint8 contentsChangedMask = WowCS::CGObjectChangedMask; - for (WowCS::EntityFragment updateableFragmentId : m_entityFragments.GetUpdateableIds()) - if (WowCS::IsIndirectFragment(updateableFragmentId)) - contentsChangedMask |= m_entityFragments.GetUpdateMaskFor(updateableFragmentId) >> 1; // set the "fragment exists" bit - - *data << uint8(flags.HasFlag(UF::UpdateFieldFlag::Owner)); - *data << uint8(false); // m_entityFragments.IdsChanged - *data << uint8(contentsChangedMask); -} - -void Object::BuildDestroyUpdateBlock(UpdateData* data) const -{ - data->AddDestroyObject(GetGUID()); -} - -void Object::BuildOutOfRangeUpdateBlock(UpdateData* data) const -{ - data->AddOutOfRangeGUID(GetGUID()); -} - -ByteBuffer& Object::PrepareValuesUpdateBuffer(UpdateData* data) const -{ - ByteBuffer& buffer = data->GetBuffer(); - buffer << uint8(UPDATETYPE_VALUES); - buffer << GetGUID(); - return buffer; -} - -void Object::DestroyForPlayer(Player* target) const -{ - ASSERT(target); - - UpdateData updateData(target->GetMapId()); - BuildDestroyUpdateBlock(&updateData); - WorldPacket packet; - updateData.BuildPacket(&packet); - target->SendDirectMessage(&packet); -} - -void Object::SendOutOfRangeForPlayer(Player* target) const -{ - ASSERT(target); - - UpdateData updateData(target->GetMapId()); - BuildOutOfRangeUpdateBlock(&updateData); - WorldPacket packet; - updateData.BuildPacket(&packet); - target->SendDirectMessage(&packet); -} - -void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const -{ - std::vector<uint32> const* PauseTimes = nullptr; - if (GameObject const* go = ToGameObject()) - PauseTimes = go->GetPauseTimes(); - - data->WriteBit(IsWorldObject()); // HasPositionFragment - data->WriteBit(flags.NoBirthAnim); - data->WriteBit(flags.EnablePortals); - data->WriteBit(flags.PlayHoverAnim); - data->WriteBit(flags.ThisIsYou); - data->WriteBit(flags.MovementUpdate); - data->WriteBit(flags.MovementTransport); - data->WriteBit(flags.Stationary); - data->WriteBit(flags.CombatVictim); - data->WriteBit(flags.ServerTime); - data->WriteBit(flags.Vehicle); - data->WriteBit(flags.AnimKit); - data->WriteBit(flags.Rotation); - data->WriteBit(flags.GameObject); - data->WriteBit(flags.SmoothPhasing); - data->WriteBit(flags.SceneObject); - data->WriteBit(flags.ActivePlayer); - data->WriteBit(flags.Conversation); - data->WriteBit(flags.Room); - data->WriteBit(flags.Decor); - data->WriteBit(flags.MeshObject); - data->FlushBits(); - - if (flags.MovementUpdate) - { - Unit const* unit = static_cast<Unit const*>(this); - bool HasFallDirection = unit->HasUnitMovementFlag(MOVEMENTFLAG_FALLING); - bool HasFall = HasFallDirection || unit->m_movementInfo.jump.fallTime != 0; - bool HasSpline = unit->IsSplineEnabled(); - bool HasInertia = unit->m_movementInfo.inertia.has_value(); - bool HasAdvFlying = unit->m_movementInfo.advFlying.has_value(); - bool HasDriveStatus = unit->m_movementInfo.driveStatus.has_value(); - bool HasStandingOnGameObjectGUID = unit->m_movementInfo.standingOnGameObjectGUID.has_value(); - - *data << GetGUID(); // MoverGUID - - *data << uint32(unit->GetUnitMovementFlags()); - *data << uint32(unit->GetExtraUnitMovementFlags()); - *data << uint32(unit->GetExtraUnitMovementFlags2()); - - *data << uint32(unit->m_movementInfo.time); // MoveTime - *data << float(unit->GetPositionX()); - *data << float(unit->GetPositionY()); - *data << float(unit->GetPositionZ()); - *data << float(unit->GetOrientation()); - - *data << float(unit->m_movementInfo.pitch); // Pitch - *data << float(unit->m_movementInfo.stepUpStartElevation); // StepUpStartElevation - - *data << uint32(0); // RemoveForcesIDs.size() - *data << uint32(0); // MoveIndex - - //for (std::size_t i = 0; i < RemoveForcesIDs.size(); ++i) - // *data << ObjectGuid(RemoveForcesIDs); - - data->WriteBit(HasStandingOnGameObjectGUID); // HasStandingOnGameObjectGUID - data->WriteBit(!unit->m_movementInfo.transport.guid.IsEmpty()); // HasTransport - data->WriteBit(HasFall); // HasFall - data->WriteBit(HasSpline); // HasSpline - marks that the unit uses spline movement - data->WriteBit(false); // HeightChangeFailed - data->WriteBit(false); // RemoteTimeValid - data->WriteBit(HasInertia); // HasInertia - data->WriteBit(HasAdvFlying); // HasAdvFlying - data->WriteBit(HasDriveStatus); // HasDriveStatus - data->FlushBits(); - - if (!unit->m_movementInfo.transport.guid.IsEmpty()) - *data << unit->m_movementInfo.transport; - - if (HasStandingOnGameObjectGUID) - *data << *unit->m_movementInfo.standingOnGameObjectGUID; - - if (HasInertia) - { - *data << unit->m_movementInfo.inertia->id; - *data << unit->m_movementInfo.inertia->force.PositionXYZStream(); - *data << uint32(unit->m_movementInfo.inertia->lifetime); - } - - if (HasAdvFlying) - { - *data << float(unit->m_movementInfo.advFlying->forwardVelocity); - *data << float(unit->m_movementInfo.advFlying->upVelocity); - } - - if (HasFall) - { - *data << uint32(unit->m_movementInfo.jump.fallTime); // Time - *data << float(unit->m_movementInfo.jump.zspeed); // JumpVelocity - - if (data->WriteBit(HasFallDirection)) - { - *data << float(unit->m_movementInfo.jump.sinAngle); // Direction - *data << float(unit->m_movementInfo.jump.cosAngle); - *data << float(unit->m_movementInfo.jump.xyspeed); // Speed - } - } - - if (HasDriveStatus) - { - *data << float(unit->m_movementInfo.driveStatus->speed); - *data << float(unit->m_movementInfo.driveStatus->movementAngle); - data->WriteBit(unit->m_movementInfo.driveStatus->accelerating); - data->WriteBit(unit->m_movementInfo.driveStatus->drifting); - data->FlushBits(); - } - - *data << float(unit->GetSpeed(MOVE_WALK)); - *data << float(unit->GetSpeed(MOVE_RUN)); - *data << float(unit->GetSpeed(MOVE_RUN_BACK)); - *data << float(unit->GetSpeed(MOVE_SWIM)); - *data << float(unit->GetSpeed(MOVE_SWIM_BACK)); - *data << float(unit->GetSpeed(MOVE_FLIGHT)); - *data << float(unit->GetSpeed(MOVE_FLIGHT_BACK)); - *data << float(unit->GetSpeed(MOVE_TURN_RATE)); - *data << float(unit->GetSpeed(MOVE_PITCH_RATE)); - - if (MovementForces const* movementForces = unit->GetMovementForces()) - { - *data << uint32(movementForces->GetForces()->size()); - *data << float(movementForces->GetModMagnitude()); // MovementForcesModMagnitude - } - else - { - *data << uint32(0); - *data << float(1.0f); // MovementForcesModMagnitude - } - - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_AIR_FRICTION)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_MAX_VEL)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LIFT_COEFFICIENT)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_DOUBLE_JUMP_VEL_MOD)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_GLIDE_START_MIN_HEIGHT)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_ADD_IMPULSE_MAX_SPEED)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_BANKING_RATE)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_BANKING_RATE)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_DOWN)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_DOWN)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_PITCHING_RATE_UP)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_PITCHING_RATE_UP)); - *data << float(unit->GetAdvFlyingSpeedMin(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); - *data << float(unit->GetAdvFlyingSpeedMax(ADV_FLYING_TURN_VELOCITY_THRESHOLD)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_SURFACE_FRICTION)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_OVER_MAX_DECELERATION)); - *data << float(unit->GetAdvFlyingSpeed(ADV_FLYING_LAUNCH_SPEED_COEFFICIENT)); - - data->WriteBit(HasSpline); - data->FlushBits(); - - if (MovementForces const* movementForces = unit->GetMovementForces()) - for (MovementForce const& force : *movementForces->GetForces()) - WorldPackets::Movement::CommonMovement::WriteMovementForceWithDirection(force, *data, unit); - - if (HasSpline) - WorldPackets::Movement::CommonMovement::WriteCreateObjectSplineDataBlock(*unit->movespline, *data); - } - - *data << uint32(PauseTimes ? PauseTimes->size() : 0); - - if (flags.Stationary) - { - WorldObject const* self = static_cast<WorldObject const*>(this); - *data << self->GetStationaryPosition().PositionXYZOStream(); - } - - if (flags.CombatVictim) - { - Unit const* unit = static_cast<Unit const*>(this); - *data << unit->GetVictim()->GetGUID(); // CombatVictim - } - - if (flags.ServerTime) - *data << uint32(GameTime::GetGameTimeMS()); - - if (flags.Vehicle) - { - Unit const* unit = static_cast<Unit const*>(this); - *data << uint32(unit->GetVehicleKit()->GetVehicleInfo()->ID); // RecID - *data << float(unit->GetOrientation()); // InitialRawFacing - } - - if (flags.AnimKit) - { - WorldObject const* self = static_cast<WorldObject const*>(this); - *data << uint16(self->GetAIAnimKitId()); // AiID - *data << uint16(self->GetMovementAnimKitId()); // MovementID - *data << uint16(self->GetMeleeAnimKitId()); // MeleeID - } - - if (flags.Rotation) + uint8 contentsChangedMask = 0; + for (std::size_t i = 0; i < m_entityFragments.UpdateableCount; ++i) { - GameObject const* gameObject = static_cast<GameObject const*>(this); - *data << uint64(gameObject->GetPackedLocalRotation()); // Rotation - } - - //if (flags.Room) - // *data << ObjectGuid(HouseGUID); + if (WowCS::IsIndirectFragment(m_entityFragments.Updateable.Ids[i])) + contentsChangedMask |= m_entityFragments.Updateable.Masks[i] >> 1; // set the "fragment exists" bit - //if (flags.Decor) - // *data << ObjectGuid(RoomGUID); - - //if (flags.MeshObject) - //{ - // *data << ObjectGuid(AttachParentGUID); - // *data << TaggedPosition<Position::XYZ>(PositionLocalSpace); - // *data << QuaternionData(RotationLocalSpace); - // *data << float(ScaleLocalSpace); - // *data << uint8(AttachmentFlags); - //} - - if (PauseTimes && !PauseTimes->empty()) - data->append(PauseTimes->data(), PauseTimes->size()); - - if (flags.MovementTransport) - { - WorldObject const* self = static_cast<WorldObject const*>(this); - *data << self->m_movementInfo.transport; + if (m_entityFragments.Updateable.Ids[i] == WowCS::EntityFragment::CGObject) + contentsChangedMask |= m_entityFragments.Updateable.Masks[i]; } - if (flags.GameObject) - { - GameObject const* gameObject = static_cast<GameObject const*>(this); - Transport const* transport = gameObject->ToTransport(); - - bool bit8 = false; - - *data << uint32(gameObject->GetWorldEffectID()); - - data->WriteBit(bit8); - data->WriteBit(transport != nullptr); - data->WriteBit(gameObject->GetPathProgressForClient().has_value()); - data->FlushBits(); - if (transport) - { - uint32 period = transport->GetTransportPeriod(); - - *data << uint32((((int64(transport->GetTimer()) - int64(GameTime::GetGameTimeMS())) % period) + period) % period); // TimeOffset - *data << uint32(transport->GetNextStopTimestamp().value_or(0)); - data->WriteBit(transport->GetNextStopTimestamp().has_value()); - data->WriteBit(transport->IsStopped()); - data->WriteBit(false); - data->FlushBits(); - } - - if (bit8) - *data << uint32(0); - - if (gameObject->GetPathProgressForClient()) - *data << float(*gameObject->GetPathProgressForClient()); - } - - if (flags.SmoothPhasing) - { - SmoothPhasingInfo const* smoothPhasingInfo = static_cast<WorldObject const*>(this)->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID()); - ASSERT(smoothPhasingInfo); - - data->WriteBit(smoothPhasingInfo->ReplaceActive); - data->WriteBit(smoothPhasingInfo->StopAnimKits); - data->WriteBit(smoothPhasingInfo->ReplaceObject.has_value()); - data->FlushBits(); - if (smoothPhasingInfo->ReplaceObject) - *data << *smoothPhasingInfo->ReplaceObject; - } - - if (flags.SceneObject) - { - data->WriteBit(false); // HasLocalScriptData - data->WriteBit(false); // HasPetBattleFullUpdate - data->FlushBits(); - - // if (HasLocalScriptData) - // { - // data->WriteBits(Data.length(), 7); - // data->FlushBits(); - // data->WriteString(Data); - // } - - // if (HasPetBattleFullUpdate) - // { - // for (std::size_t i = 0; i < 2; ++i) - // { - // *data << ObjectGuid(Players[i].CharacterID); - // *data << int32(Players[i].TrapAbilityID); - // *data << int32(Players[i].TrapStatus); - // *data << uint16(Players[i].RoundTimeSecs); - // *data << int8(Players[i].FrontPet); - // *data << uint8(Players[i].InputFlags); - - // data->WriteBits(Players[i].Pets.size(), 2); - // data->FlushBits(); - // for (std::size_t j = 0; j < Players[i].Pets.size(); ++j) - // { - // *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID); - // *data << int32(Players[i].Pets[j].SpeciesID); - // *data << int32(Players[i].Pets[j].CreatureID); - // *data << int32(Players[i].Pets[j].DisplayID); - // *data << int16(Players[i].Pets[j].Level); - // *data << int16(Players[i].Pets[j].Xp); - // *data << int32(Players[i].Pets[j].CurHealth); - // *data << int32(Players[i].Pets[j].MaxHealth); - // *data << int32(Players[i].Pets[j].Power); - // *data << int32(Players[i].Pets[j].Speed); - // *data << int32(Players[i].Pets[j].NpcTeamMemberID); - // *data << uint8(Players[i].Pets[j].BreedQuality); - // *data << uint16(Players[i].Pets[j].StatusFlags); - // *data << int8(Players[i].Pets[j].Slot); - - // *data << uint32(Players[i].Pets[j].Abilities.size()); - // *data << uint32(Players[i].Pets[j].Auras.size()); - // *data << uint32(Players[i].Pets[j].States.size()); - // for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k) - // { - // *data << int32(Players[i].Pets[j].Abilities[k].AbilityID); - // *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining); - // *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining); - // *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex); - // *data << uint8(Players[i].Pets[j].Abilities[k].Pboid); - // } - - // for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k) - // { - // *data << int32(Players[i].Pets[j].Auras[k].AbilityID); - // *data << uint32(Players[i].Pets[j].Auras[k].InstanceID); - // *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining); - // *data << int32(Players[i].Pets[j].Auras[k].CurrentRound); - // *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID); - // } - - // for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k) - // { - // *data << uint32(Players[i].Pets[j].States[k].StateID); - // *data << int32(Players[i].Pets[j].States[k].StateValue); - // } - - // data->WriteBits(Players[i].Pets[j].CustomName.length(), 7); - // data->FlushBits(); - // data->WriteString(Players[i].Pets[j].CustomName); - // } - // } - - // for (std::size_t i = 0; i < 3; ++i) - // { - // *data << uint32(Enviros[j].Auras.size()); - // *data << uint32(Enviros[j].States.size()); - // for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j) - // { - // *data << int32(Enviros[j].Auras[j].AbilityID); - // *data << uint32(Enviros[j].Auras[j].InstanceID); - // *data << int32(Enviros[j].Auras[j].RoundsRemaining); - // *data << int32(Enviros[j].Auras[j].CurrentRound); - // *data << uint8(Enviros[j].Auras[j].CasterPBOID); - // } - - // for (std::size_t j = 0; j < Enviros[j].States.size(); ++j) - // { - // *data << uint32(Enviros[i].States[j].StateID); - // *data << int32(Enviros[i].States[j].StateValue); - // } - // } - - // *data << uint16(WaitingForFrontPetsMaxSecs); - // *data << uint16(PvpMaxRoundTime); - // *data << int32(CurRound); - // *data << uint32(NpcCreatureID); - // *data << uint32(NpcDisplayID); - // *data << int8(CurPetBattleState); - // *data << uint8(ForfeitPenalty); - // *data << ObjectGuid(InitialWildPetGUID); - // data->WriteBit(IsPVP); - // data->WriteBit(CanAwardXP); - // data->FlushBits(); - // } - } - - if (flags.ActivePlayer) - { - Player const* player = static_cast<Player const*>(this); - - bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty(); - bool HasRuneState = player->GetPowerIndex(POWER_RUNES) != MAX_POWERS; - - data->WriteBit(HasSceneInstanceIDs); - data->WriteBit(HasRuneState); - data->FlushBits(); - if (HasSceneInstanceIDs) - { - *data << uint32(player->GetSceneMgr().GetSceneTemplateByInstanceMap().size()); - for (auto const& [sceneInstanceId, _] : player->GetSceneMgr().GetSceneTemplateByInstanceMap()) - *data << uint32(sceneInstanceId); - } - if (HasRuneState) - { - float baseCd = float(player->GetRuneBaseCooldown()); - uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES)); - - *data << uint8((1 << maxRunes) - 1); - *data << uint8(player->GetRunesState()); - *data << uint32(maxRunes); - for (uint32 i = 0; i < maxRunes; ++i) - *data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255); - } - } - - if (flags.Conversation) - { - Conversation const* self = static_cast<Conversation const*>(this); - if (data->WriteBit(self->GetTextureKitId() != 0)) - *data << uint32(self->GetTextureKitId()); - - data->FlushBits(); - } -} - -UF::UpdateFieldFlag Object::GetUpdateFieldFlagsFor(Player const* /*target*/) const -{ - return UF::UpdateFieldFlag::None; + *data << uint8(flags.HasFlag(UF::UpdateFieldFlag::Owner)); + *data << uint8(false); // m_entityFragments.IdsChanged + *data << uint8(contentsChangedMask); } void Object::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag /*flags*/, Player const* /*target*/) const @@ -725,36 +123,32 @@ void Object::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag /*f *data << uint32(0); } -void Object::AddToObjectUpdateIfNeeded() +void Object::ClearUpdateMask(bool remove) { - if (m_inWorld && !m_objectUpdated) - m_objectUpdated = AddToObjectUpdate(); + m_values.ClearChangesMask(&Object::m_objectData); + BaseEntity::ClearUpdateMask(remove); } -void Object::ClearUpdateMask(bool remove) +void Object::BuildObjectFragmentCreate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) { - m_values.ClearChangesMask(&Object::m_objectData); - m_entityFragments.IdsChanged = false; + static_cast<Object const*>(entity)->BuildValuesCreate(&data, flags, target); +} - if (m_objectUpdated) - { - if (remove) - RemoveFromObjectUpdate(); - m_objectUpdated = false; - } +void Object::BuildObjectFragmentUpdate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) +{ + static_cast<Object const*>(entity)->BuildValuesUpdate(&data, flags, target); } -void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const +bool Object::IsObjectFragmentChanged(BaseEntity const* entity) { - UpdateDataMapType::iterator iter = data_map.try_emplace(player, player->GetMapId()).first; - BuildValuesUpdateBlockForPlayer(&iter->second, iter->first); + return entity->m_values.GetChangedObjectTypeMask() != 0; } std::string Object::GetDebugInfo() const { std::stringstream sstr; - sstr << GetGUID().ToString() + " Entry " << GetEntry(); - return sstr.str(); + sstr << BaseEntity::GetDebugInfo() << " Entry " << GetEntry(); + return std::move(sstr).str(); } void MovementInfo::OutDebug() @@ -814,6 +208,8 @@ m_movementInfo(), m_name(), m_isActive(false), m_isFarVisible(false), m_isStored m_transport(nullptr), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_outdoors(false), m_liquidStatus(LIQUID_MAP_NO_WATER), m_currMap(nullptr), m_InstanceId(0), _dbPhase(0), m_notifyflags(0), _heartbeatTimer(HEARTBEAT_INTERVAL) { + m_updateFlag.HasEntityPosition = true; + m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST); m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE); } @@ -1401,29 +797,32 @@ float WorldObject::GetVisibilityRange() const float WorldObject::GetSightRange(WorldObject const* target) const { - if (ToUnit()) + if (IsUnit()) { - if (ToPlayer()) + if (Player const* player = ToPlayer()) { - if (target && target->IsVisibilityOverridden() && !target->ToPlayer()) - return *target->m_visibilityDistanceOverride; - else if (target && target->IsFarVisible() && !target->ToPlayer()) - return MAX_VISIBILITY_DISTANCE; - else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) + if (target && !target->IsPlayer()) + { + if (target->IsVisibilityOverridden()) + return *target->m_visibilityDistanceOverride; + if (target->IsFarVisible()) + return MAX_VISIBILITY_DISTANCE; + } + + if (player->GetCinematicMgr()->IsOnCinematic()) return DEFAULT_VISIBILITY_INSTANCE; - else - return GetMap()->GetVisibilityRange(); + + return GetMap()->GetVisibilityRange(); } - else if (ToCreature()) - return ToCreature()->m_SightDistance; - else - return SIGHT_RANGE_UNIT; + + if (Creature const* creature = ToCreature()) + return creature->m_SightDistance; + + return SIGHT_RANGE_UNIT; } - if (ToDynObject() && isActiveObject()) - { + if (IsDynObject() && isActiveObject()) return GetMap()->GetVisibilityRange(); - } return 0.0f; } @@ -2193,8 +1592,8 @@ ObjectGuid WorldObject::GetCharmerOrOwnerOrOwnGUID() const { ObjectGuid guid = GetCharmerOrOwnerGUID(); if (!guid.IsEmpty()) - return guid; - return GetGUID(); + guid = GetGUID(); + return guid; } Unit* WorldObject::GetOwner() const @@ -2591,10 +1990,10 @@ SpellMissInfo WorldObject::MagicSpellHitResult(Unit* victim, SpellInfo const* sp // Parry // For spells // Resist -SpellMissInfo WorldObject::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/) const +SpellMissInfo WorldObject::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect, bool canImmune) const { // Check for immune - if (victim->IsImmunedToSpell(spellInfo, this)) + if (canImmune && victim->IsImmunedToSpell(spellInfo, MAX_EFFECT_MASK, this)) return SPELL_MISS_IMMUNE; // Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply @@ -3721,6 +3120,8 @@ struct WorldObjectChangeAccumulator void WorldObject::BuildUpdate(UpdateDataMapType& data_map) { + BuildUpdateChangesMask(); + WorldObjectChangeAccumulator notifier(*this, data_map); WorldObjectVisibleChangeVisitor visitor(notifier); //we must build packets for all visible players @@ -3747,6 +3148,12 @@ ObjectGuid WorldObject::GetTransGUID() const return ObjectGuid::Empty; } +void WorldObject::SetTransport(TransportBase* t) +{ + m_transport = t; + m_updateFlag.MovementTransport = !m_updateFlag.MovementUpdate && t != nullptr; +} + float WorldObject::GetFloorZ() const { if (!IsInWorld()) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 72b3a5dd448..9be5f274dc0 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -18,6 +18,7 @@ #ifndef _OBJECT_H #define _OBJECT_H +#include "BaseEntity.h" #include "Common.h" #include "Duration.h" #include "Errors.h" @@ -26,17 +27,13 @@ #include "ModelIgnoreFlags.h" #include "MovementInfo.h" #include "ObjectDefines.h" -#include "ObjectGuid.h" #include "Optional.h" #include "PhaseShift.h" #include "Position.h" #include "SharedDefines.h" #include "SpellDefines.h" #include "UniqueTrackablePtr.h" -#include "UpdateFields.h" -#include "WowCSEntityDefinitions.h" #include <list> -#include <unordered_map> class AreaTrigger; class Conversation; @@ -78,132 +75,17 @@ namespace WorldPackets } } -typedef std::unordered_map<Player*, UpdateData> UpdateDataMapType; - -struct CreateObjectBits -{ - bool NoBirthAnim : 1; - bool EnablePortals : 1; - bool PlayHoverAnim : 1; - bool MovementUpdate : 1; - bool MovementTransport : 1; - bool Stationary : 1; - bool CombatVictim : 1; - bool ServerTime : 1; - bool Vehicle : 1; - bool AnimKit : 1; - bool Rotation : 1; - bool GameObject : 1; - bool SmoothPhasing : 1; - bool ThisIsYou : 1; - bool SceneObject : 1; - bool ActivePlayer : 1; - bool Conversation : 1; - bool Room : 1; - bool Decor : 1; - bool MeshObject : 1; - - void Clear() - { - memset(this, 0, sizeof(CreateObjectBits)); - } -}; - -namespace UF -{ - class UpdateFieldHolder - { - public: - template<typename Derived, typename T, int32 BlockBit, uint32 Bit> - inline MutableFieldReference<T, false> ModifyValue(UpdateField<T, BlockBit, Bit>(Derived::* field)); - - template<typename Derived, typename T, int32 BlockBit, uint32 Bit> - inline OptionalUpdateFieldSetter<T> ModifyValue(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field)); - - template<typename Derived, typename T, int32 BlockBit, uint32 Bit> - inline MutableFieldReference<T, false> ModifyValue(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field), uint32 /*dummy*/); - - template<typename Derived, typename T, int32 BlockBit, uint32 Bit> - inline void ClearChangesMask(UpdateField<T, BlockBit, Bit>(Derived::* field)); - - template<typename Derived, typename T, int32 BlockBit, uint32 Bit> - inline void ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit>(Derived::* field)); - - uint32 GetChangedObjectTypeMask() const { return _changesMask; } - - bool HasChanged(uint32 index) const { return (_changesMask & UpdateMaskHelpers::GetBlockFlag(index)) != 0; } - - inline Object* GetOwner(); - - private: - friend Object; - - // This class is tightly tied to Object::m_values member, do not construct elsewhere - UpdateFieldHolder() : _changesMask(0) { } - - uint32 _changesMask; - }; - - template<typename T> - inline bool SetUpdateFieldValue(UpdateFieldPrivateSetter<T>& setter, typename UpdateFieldPrivateSetter<T>::value_type&& value) - { - return setter.SetValue(std::move(value)); - } - - template<typename T> - inline typename DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter) - { - return setter.AddValue(); - } - - template<typename T> - inline typename DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) - { - return setter.InsertValue(index); - } - - template<typename T> - inline void RemoveDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index) - { - setter.RemoveValue(index); - } - - template<typename T> - inline void ClearDynamicUpdateFieldValues(DynamicUpdateFieldSetter<T>& setter) - { - setter.Clear(); - } - - template<typename K, typename V> - inline void RemoveMapUpdateFieldValue(MapUpdateFieldSetter<K, V>& setter, std::type_identity_t<K> const& key) - { - setter.RemoveKey(key); - } - - template<typename T> - inline void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter<T>& setter) - { - setter.RemoveValue(); - } -} - float const DEFAULT_COLLISION_HEIGHT = 2.03128f; // Most common value in dbc static constexpr Milliseconds const HEARTBEAT_INTERVAL = 5s + 200ms; -class TC_GAME_API Object +class TC_GAME_API Object : public BaseEntity { - ObjectGuid m_guid; - public: virtual ~Object(); - bool IsInWorld() const { return m_inWorld; } - - virtual void AddToWorld(); - virtual void RemoveFromWorld(); + void AddToWorld() override; + void RemoveFromWorld() override; - static ObjectGuid GetGUID(Object const* o) { return o ? o->GetGUID() : ObjectGuid::Empty; } - ObjectGuid const& GetGUID() const { return m_guid; } uint32 GetEntry() const { return m_objectData->EntryID; } void SetEntry(uint32 entry) { SetUpdateFieldValue(m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::EntryID), entry); } @@ -216,110 +98,71 @@ class TC_GAME_API Object void RemoveDynamicFlag(uint32 flag) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags), flag); } void ReplaceAllDynamicFlags(uint32 flag) { SetUpdateFieldValue(m_values.ModifyValue(&Object::m_objectData).ModifyValue(&UF::ObjectData::DynamicFlags), flag); } - TypeID GetTypeId() const { return m_objectTypeId; } - bool isType(TypeMask mask) const { return (ObjectTypeMask[m_objectTypeId] & mask) != 0; } - - virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const; - void SendUpdateToPlayer(Player* player); - - void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const; void BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::UpdateFieldFlag flags, Player const* target) const; - void BuildDestroyUpdateBlock(UpdateData* data) const; - void BuildOutOfRangeUpdateBlock(UpdateData* data) const; - ByteBuffer& PrepareValuesUpdateBuffer(UpdateData* data) const; - virtual void DestroyForPlayer(Player* target) const; - void SendOutOfRangeForPlayer(Player* target) const; - - virtual void ClearUpdateMask(bool remove); - - virtual std::string GetNameForLocaleIdx(LocaleConstant locale) const = 0; + void ClearUpdateMask(bool remove) override; virtual bool hasQuest(uint32 /* quest_id */) const { return false; } virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; } - void SetIsNewObject(bool enable) { m_isNewObject = enable; } - bool IsDestroyedObject() const { return m_isDestroyedObject; } - void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; } - virtual void BuildUpdate(UpdateDataMapType&) { } - void BuildFieldsUpdate(Player*, UpdateDataMapType &) const; - - inline bool IsWorldObject() const { return isType(TYPEMASK_WORLDOBJECT); } - static WorldObject* ToWorldObject(Object* o) { return o ? o->ToWorldObject() : nullptr; } - static WorldObject const* ToWorldObject(Object const* o) { return o ? o->ToWorldObject() : nullptr; } - WorldObject* ToWorldObject() { if (IsWorldObject()) return reinterpret_cast<WorldObject*>(this); else return nullptr; } - WorldObject const* ToWorldObject() const { if (IsWorldObject()) return reinterpret_cast<WorldObject const*>(this); else return nullptr; } - - inline bool IsItem() const { return isType(TYPEMASK_ITEM); } - static Item* ToItem(Object* o) { return o ? o->ToItem() : nullptr; } - static Item const* ToItem(Object const* o) { return o ? o->ToItem() : nullptr; } - Item* ToItem() { if (IsItem()) return reinterpret_cast<Item*>(this); else return nullptr; } - Item const* ToItem() const { if (IsItem()) return reinterpret_cast<Item const*>(this); else return nullptr; } - - inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; } - static Player* ToPlayer(Object* o) { return o ? o->ToPlayer() : nullptr; } - static Player const* ToPlayer(Object const* o) { return o ? o->ToPlayer() : nullptr; } - Player* ToPlayer() { if (IsPlayer()) return reinterpret_cast<Player*>(this); else return nullptr; } - Player const* ToPlayer() const { if (IsPlayer()) return reinterpret_cast<Player const*>(this); else return nullptr; } - - inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; } - static Creature* ToCreature(Object* o) { return o ? o->ToCreature() : nullptr; } - static Creature const* ToCreature(Object const* o) { return o ? o->ToCreature() : nullptr; } - Creature* ToCreature() { if (IsCreature()) return reinterpret_cast<Creature*>(this); else return nullptr; } - Creature const* ToCreature() const { if (IsCreature()) return reinterpret_cast<Creature const*>(this); else return nullptr; } - - inline bool IsUnit() const { return isType(TYPEMASK_UNIT); } - static Unit* ToUnit(Object* o) { return o ? o->ToUnit() : nullptr; } - static Unit const* ToUnit(Object const* o) { return o ? o->ToUnit() : nullptr; } - Unit* ToUnit() { if (IsUnit()) return reinterpret_cast<Unit*>(this); else return nullptr; } - Unit const* ToUnit() const { if (IsUnit()) return reinterpret_cast<Unit const*>(this); else return nullptr; } - - inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; } - static GameObject* ToGameObject(Object* o) { return o ? o->ToGameObject() : nullptr; } - static GameObject const* ToGameObject(Object const* o) { return o ? o->ToGameObject() : nullptr; } - GameObject* ToGameObject() { if (IsGameObject()) return reinterpret_cast<GameObject*>(this); else return nullptr; } - GameObject const* ToGameObject() const { if (IsGameObject()) return reinterpret_cast<GameObject const*>(this); else return nullptr; } - - inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; } - static Corpse* ToCorpse(Object* o) { return o ? o->ToCorpse() : nullptr; } - static Corpse const* ToCorpse(Object const* o) { return o ? o->ToCorpse() : nullptr; } - Corpse* ToCorpse() { if (IsCorpse()) return reinterpret_cast<Corpse*>(this); else return nullptr; } - Corpse const* ToCorpse() const { if (IsCorpse()) return reinterpret_cast<Corpse const*>(this); else return nullptr; } - - inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; } - static DynamicObject* ToDynObject(Object* o) { return o ? o->ToDynObject() : nullptr; } - static DynamicObject const* ToDynObject(Object const* o) { return o ? o->ToDynObject() : nullptr; } - DynamicObject* ToDynObject() { if (IsDynObject()) return reinterpret_cast<DynamicObject*>(this); else return nullptr; } - DynamicObject const* ToDynObject() const { if (IsDynObject()) return reinterpret_cast<DynamicObject const*>(this); else return nullptr; } - - inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; } - static AreaTrigger* ToAreaTrigger(Object* o) { return o ? o->ToAreaTrigger() : nullptr; } - static AreaTrigger const* ToAreaTrigger(Object const* o) { return o ? o->ToAreaTrigger() : nullptr; } - AreaTrigger* ToAreaTrigger() { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger*>(this); else return nullptr; } - AreaTrigger const* ToAreaTrigger() const { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger const*>(this); else return nullptr; } - - inline bool IsSceneObject() const { return GetTypeId() == TYPEID_SCENEOBJECT; } - static SceneObject* ToSceneObject(Object* o) { return o ? o->ToSceneObject() : nullptr; } - static SceneObject const* ToSceneObject(Object const* o) { return o ? o->ToSceneObject() : nullptr; } - SceneObject* ToSceneObject() { if (IsSceneObject()) return reinterpret_cast<SceneObject*>(this); else return nullptr; } - SceneObject const* ToSceneObject() const { if (IsSceneObject()) return reinterpret_cast<SceneObject const*>(this); else return nullptr; } - - inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; } - static Conversation* ToConversation(Object* o) { return o ? o->ToConversation() : nullptr; } - static Conversation const* ToConversation(Object const* o) { return o ? o->ToConversation() : nullptr; } - Conversation* ToConversation() { if (IsConversation()) return reinterpret_cast<Conversation*>(this); else return nullptr; } - Conversation const* ToConversation() const { if (IsConversation()) return reinterpret_cast<Conversation const*>(this); else return nullptr; } - - friend UF::UpdateFieldHolder; - UF::UpdateFieldHolder m_values; - UF::UpdateField<UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT> m_objectData; - template<typename T> - void ForceUpdateFieldChange(UF::UpdateFieldPrivateSetter<T> const& /*setter*/) - { - AddToObjectUpdateIfNeeded(); - } + WorldObject* ToWorldObject() { return IsWorldObject() ? reinterpret_cast<WorldObject*>(this) : nullptr; } + WorldObject const* ToWorldObject() const { return IsWorldObject() ? reinterpret_cast<WorldObject const*>(this) : nullptr; } + static WorldObject* ToWorldObject(Object* o) { return o && o->IsWorldObject() ? reinterpret_cast<WorldObject*>(o) : nullptr; } + static WorldObject const* ToWorldObject(Object const* o) { return o && o->IsWorldObject() ? reinterpret_cast<WorldObject const*>(o) : nullptr; } + + Item* ToItem() { return IsItem() ? reinterpret_cast<Item*>(this) : nullptr; } + Item const* ToItem() const { return IsItem() ? reinterpret_cast<Item const*>(this) : nullptr; } + static Item* ToItem(Object* o) { return o && o->IsItem() ? reinterpret_cast<Item*>(o) : nullptr; } + static Item const* ToItem(Object const* o) { return o && o->IsItem() ? reinterpret_cast<Item const*>(o) : nullptr; } + + Unit* ToUnit() { return IsUnit() ? reinterpret_cast<Unit*>(this) : nullptr; } + Unit const* ToUnit() const { return IsUnit() ? reinterpret_cast<Unit const*>(this) : nullptr; } + static Unit* ToUnit(Object* o) { return o && o->IsUnit() ? reinterpret_cast<Unit*>(o) : nullptr; } + static Unit const* ToUnit(Object const* o) { return o && o->IsUnit() ? reinterpret_cast<Unit const*>(o) : nullptr; } + + Creature* ToCreature() { return IsCreature() ? reinterpret_cast<Creature*>(this) : nullptr; } + Creature const* ToCreature() const { return IsCreature() ? reinterpret_cast<Creature const*>(this) : nullptr; } + static Creature* ToCreature(Object* o) { return o && o->IsCreature() ? reinterpret_cast<Creature*>(o) : nullptr; } + static Creature const* ToCreature(Object const* o) { return o && o->IsCreature() ? reinterpret_cast<Creature const*>(o) : nullptr; } + + Player* ToPlayer() { return IsPlayer() ? reinterpret_cast<Player*>(this) : nullptr; } + Player const* ToPlayer() const { return IsPlayer() ? reinterpret_cast<Player const*>(this) : nullptr; } + static Player* ToPlayer(Object* o) { return o && o->IsPlayer() ? reinterpret_cast<Player*>(o) : nullptr; } + static Player const* ToPlayer(Object const* o) { return o && o->IsPlayer() ? reinterpret_cast<Player const*>(o) : nullptr; } + + GameObject* ToGameObject() { return IsGameObject() ? reinterpret_cast<GameObject*>(this) : nullptr; } + GameObject const* ToGameObject() const { return IsGameObject() ? reinterpret_cast<GameObject const*>(this) : nullptr; } + static GameObject* ToGameObject(Object* o) { return o && o->IsGameObject() ? reinterpret_cast<GameObject*>(o) : nullptr; } + static GameObject const* ToGameObject(Object const* o) { return o && o->IsGameObject() ? reinterpret_cast<GameObject const*>(o) : nullptr; } + + Corpse* ToCorpse() { return IsCorpse() ? reinterpret_cast<Corpse*>(this) : nullptr; } + Corpse const* ToCorpse() const { return IsCorpse() ? reinterpret_cast<Corpse const*>(this) : nullptr; } + static Corpse* ToCorpse(Object* o) { return o && o->IsCorpse() ? reinterpret_cast<Corpse*>(o) : nullptr; } + static Corpse const* ToCorpse(Object const* o) { return o && o->IsCorpse() ? reinterpret_cast<Corpse const*>(o) : nullptr; } + + DynamicObject* ToDynObject() { return IsDynObject() ? reinterpret_cast<DynamicObject*>(this) : nullptr; } + DynamicObject const* ToDynObject() const { return IsDynObject() ? reinterpret_cast<DynamicObject const*>(this) : nullptr; } + static DynamicObject* ToDynObject(Object* o) { return o && o->IsDynObject() ? reinterpret_cast<DynamicObject*>(o) : nullptr; } + static DynamicObject const* ToDynObject(Object const* o) { return o && o->IsDynObject() ? reinterpret_cast<DynamicObject const*>(o) : nullptr; } + + AreaTrigger* ToAreaTrigger() { return IsAreaTrigger() ? reinterpret_cast<AreaTrigger*>(this) : nullptr; } + AreaTrigger const* ToAreaTrigger() const { return IsAreaTrigger() ? reinterpret_cast<AreaTrigger const*>(this) : nullptr; } + static AreaTrigger* ToAreaTrigger(Object* o) { return o && o->IsAreaTrigger() ? reinterpret_cast<AreaTrigger*>(o) : nullptr; } + static AreaTrigger const* ToAreaTrigger(Object const* o) { return o && o->IsAreaTrigger() ? reinterpret_cast<AreaTrigger const*>(o) : nullptr; } + + SceneObject* ToSceneObject() { return IsSceneObject() ? reinterpret_cast<SceneObject*>(this) : nullptr; } + SceneObject const* ToSceneObject() const { return IsSceneObject() ? reinterpret_cast<SceneObject const*>(this) : nullptr; } + static SceneObject* ToSceneObject(Object* o) { return o && o->IsSceneObject() ? reinterpret_cast<SceneObject*>(o) : nullptr; } + static SceneObject const* ToSceneObject(Object const* o) { return o && o->IsSceneObject() ? reinterpret_cast<SceneObject const*>(o) : nullptr; } + + Conversation* ToConversation() { return IsConversation() ? reinterpret_cast<Conversation*>(this) : nullptr; } + Conversation const* ToConversation() const { return IsConversation() ? reinterpret_cast<Conversation const*>(this) : nullptr; } + static Conversation* ToConversation(Object* o) { return o && o->IsConversation() ? reinterpret_cast<Conversation*>(o) : nullptr; } + static Conversation const* ToConversation(Object const* o) { return o && o->IsConversation() ? reinterpret_cast<Conversation const*>(o) : nullptr; } - virtual std::string GetDebugInfo() const; + UF::UpdateField<UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT> m_objectData; + + std::string GetDebugInfo() const override; Trinity::unique_weak_ptr<Object> GetWeakPtr() const { return m_scriptRef; } @@ -330,242 +173,22 @@ class TC_GAME_API Object protected: Object(); - void _Create(ObjectGuid const& guid); - - template<typename T> - void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) - { - if (UF::SetUpdateFieldValue(setter, std::move(value))) - AddToObjectUpdateIfNeeded(); - } - - template<typename T> - void SetUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) - { - static_assert(std::is_integral<T>::value, "SetUpdateFieldFlagValue must be used with integral types"); - SetUpdateFieldValue(setter, setter.GetValue() | flag); - } - - template<typename T> - void RemoveUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag) - { - static_assert(std::is_integral<T>::value, "RemoveUpdateFieldFlagValue must be used with integral types"); - SetUpdateFieldValue(setter, setter.GetValue() & ~flag); - } - - template<typename T> - typename UF::DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter) - { - AddToObjectUpdateIfNeeded(); - return UF::AddDynamicUpdateFieldValue(setter); - } - - template<typename T> - typename UF::DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) - { - AddToObjectUpdateIfNeeded(); - return UF::InsertDynamicUpdateFieldValue(setter, index); - } - - template<typename T> - void RemoveDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveDynamicUpdateFieldValue(setter, index); - } - - template<typename K, typename V> - void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter<K, V> setter, std::type_identity_t<K> const& key) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveMapUpdateFieldValue(setter, key); - } - - template<typename T> - void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter<T> setter) - { - AddToObjectUpdateIfNeeded(); - UF::ClearDynamicUpdateFieldValues(setter); - } - - template<typename T> - void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter<T> setter) - { - AddToObjectUpdateIfNeeded(); - UF::RemoveOptionalUpdateFieldValue(setter); - } - - // stat system helpers - template<typename T> - void SetUpdateFieldStatValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value) - { - static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types"); - SetUpdateFieldValue(setter, std::max(value, T(0))); - } - - template<typename T> - void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type mod, bool apply) - { - static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types"); - - T value = setter.GetValue(); - if (apply) - value += mod; - else - value -= mod; - - SetUpdateFieldValue(setter, value); - } - - template<typename T> - void ApplyPercentModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, float percent, bool apply) - { - static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types"); - - T value = setter.GetValue(); - - // don't want to include Util.h here - //ApplyPercentModFloatVar(value, percent, apply); - if (percent == -100.0f) - percent = -99.99f; - value *= (apply ? (100.0f + percent) / 100.0f : 100.0f / (100.0f + percent)); - - SetUpdateFieldValue(setter, value); - } - - template<typename Action> - void DoWithSuppressingObjectUpdates(Action&& action) - { - bool wasUpdatedBeforeAction = m_objectUpdated; - action(); - if (m_objectUpdated && !wasUpdatedBeforeAction) - { - RemoveFromObjectUpdate(); - m_objectUpdated = false; - } - } - - void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const; - virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const; virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0; - static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments); void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const; public: virtual void BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const; - protected: - TypeID m_objectTypeId; - CreateObjectBits m_updateFlag; - WowCS::EntityFragmentsHolder m_entityFragments; - - virtual bool AddToObjectUpdate() = 0; - virtual void RemoveFromObjectUpdate() = 0; - void AddToObjectUpdateIfNeeded(); - - bool m_objectUpdated; - private: - bool m_inWorld; - bool m_isNewObject; - bool m_isDestroyedObject; + static void BuildObjectFragmentCreate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target); + static void BuildObjectFragmentUpdate(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target); + static bool IsObjectFragmentChanged(BaseEntity const* entity); struct NoopObjectDeleter { void operator()(Object*) const { /*noop - not managed*/ } }; Trinity::unique_trackable_ptr<Object> m_scriptRef; - - Object(Object const& right) = delete; - Object(Object&& right) = delete; - Object& operator=(Object const& right) = delete; - Object& operator=(Object&& right) = delete; }; -inline Object* UF::UpdateFieldHolder::GetOwner() -{ -#if TRINITY_COMPILER == TRINITY_COMPILER_GNU -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Winvalid-offsetof" -#endif - - return reinterpret_cast<Object*>(reinterpret_cast<std::byte*>(this) - offsetof(Object, m_values)); - -#if TRINITY_COMPILER == TRINITY_COMPILER_GNU -#pragma GCC diagnostic pop -#endif -} - -template <typename Derived, typename T, int32 BlockBit, uint32 Bit> -inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field) -{ - Object* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); - - return { (static_cast<Derived*>(owner)->*field)._value }; -} - -template <typename Derived, typename T, int32 BlockBit, uint32 Bit> -inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) -{ - Object* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); - - return { static_cast<Derived*>(owner)->*field }; -} - -template <typename Derived, typename T, int32 BlockBit, uint32 Bit> -inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field, uint32 /*dummy*/) -{ - Object* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); - - auto& uf = (static_cast<Derived*>(owner)->*field); - if (!uf.has_value()) - uf.ConstructValue(); - - return { *uf._value }; -} - -template <typename Derived, typename T, int32 BlockBit, uint32 Bit> -inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit> Derived::* field) -{ - Object* owner = GetOwner(); - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - { - _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); - if (!_changesMask) - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - } - else - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - - (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask(); -} - -template <typename Derived, typename T, int32 BlockBit, uint32 Bit> -inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field) -{ - Object* owner = GetOwner(); - if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) - { - _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit); - if (!_changesMask) - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - } - else - owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); - - auto& uf = (static_cast<Derived*>(owner)->*field); - if (uf.has_value()) - uf._value->ClearChangesMask(); -} - template <class T_VALUES, class T_FLAGS, class FLAG_TYPE, size_t ARRAY_SIZE> class FlaggedValuesArray32 { @@ -837,7 +460,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const; virtual SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; - SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false) const; + SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect, bool canImmune) const; void SendSpellMiss(Unit* target, uint32 spellID, SpellMissInfo missInfo); virtual uint32 GetFaction() const = 0; @@ -921,7 +544,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation uint32 GetTransTime() const { return m_movementInfo.transport.time; } int8 GetTransSeat() const { return m_movementInfo.transport.seat; } virtual ObjectGuid GetTransGUID() const; - void SetTransport(TransportBase* t) { m_transport = t; } + void SetTransport(TransportBase* t); MovementInfo m_movementInfo; diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h index 98cf4c5632a..b5f9b84e29a 100644 --- a/src/server/game/Entities/Object/ObjectGuid.h +++ b/src/server/game/Entities/Object/ObjectGuid.h @@ -51,10 +51,10 @@ enum TypeID : uint8 TYPEID_MESH_OBJECT = 14, TYPEID_AI_GROUP = 15, TYPEID_SCENARIO = 16, - TYPEID_LOOT_OBJECT = 17 -}; + TYPEID_LOOT_OBJECT = 17, -#define NUM_CLIENT_OBJECT_TYPES 18 + NUM_CLIENT_OBJECT_TYPES +}; enum TypeMask { @@ -78,10 +78,10 @@ enum TypeMask TYPEMASK_LOOT_OBJECT = 1 << TYPEID_LOOT_OBJECT, TYPEMASK_SEER = TYPEMASK_UNIT | TYPEMASK_PLAYER | TYPEMASK_DYNAMICOBJECT, - TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION + TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION | TYPEMASK_MESH_OBJECT }; -inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES> ObjectTypeMask = +inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES + 1> ObjectTypeMask = { TYPEMASK_OBJECT, TYPEMASK_OBJECT | TYPEMASK_ITEM, @@ -101,6 +101,7 @@ inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES> ObjectTypeMask = TYPEMASK_OBJECT | TYPEMASK_AI_GROUP, TYPEMASK_OBJECT | TYPEMASK_SCENARIO, TYPEMASK_OBJECT | TYPEMASK_LOOT_OBJECT, + 0, }; enum class HighGuid diff --git a/src/server/game/Entities/Object/SmoothPhasing.cpp b/src/server/game/Entities/Object/SmoothPhasing.cpp index 4b5377eb043..a1b504a5b78 100644 --- a/src/server/game/Entities/Object/SmoothPhasing.cpp +++ b/src/server/game/Entities/Object/SmoothPhasing.cpp @@ -18,6 +18,13 @@ #include "SmoothPhasing.h" #include "MapUtils.h" +SmoothPhasing::SmoothPhasing() = default; +SmoothPhasing::SmoothPhasing(SmoothPhasing const&) = default; +SmoothPhasing::SmoothPhasing(SmoothPhasing&&) noexcept = default; +SmoothPhasing& SmoothPhasing::operator=(SmoothPhasing const&) = default; +SmoothPhasing& SmoothPhasing::operator=(SmoothPhasing&&) noexcept = default; +SmoothPhasing::~SmoothPhasing() = default; + void SmoothPhasing::SetViewerDependentInfo(ObjectGuid seer, SmoothPhasingInfo const& info) { if (!std::holds_alternative<SmoothPhasingInfoViewerDependent>(_storage)) diff --git a/src/server/game/Entities/Object/SmoothPhasing.h b/src/server/game/Entities/Object/SmoothPhasing.h index 6d211e8da28..520a6d97126 100644 --- a/src/server/game/Entities/Object/SmoothPhasing.h +++ b/src/server/game/Entities/Object/SmoothPhasing.h @@ -37,6 +37,13 @@ struct SmoothPhasingInfo class TC_GAME_API SmoothPhasing { public: + SmoothPhasing(); + SmoothPhasing(SmoothPhasing const&); + SmoothPhasing(SmoothPhasing&&) noexcept; + SmoothPhasing& operator=(SmoothPhasing const&); + SmoothPhasing& operator=(SmoothPhasing&&) noexcept; + ~SmoothPhasing(); + void SetViewerDependentInfo(ObjectGuid seer, SmoothPhasingInfo const& info); void ClearViewerDependentInfo(ObjectGuid seer); diff --git a/src/server/game/Entities/Object/Updates/UpdateData.cpp b/src/server/game/Entities/Object/Updates/UpdateData.cpp index 34fe6d86f89..1b342102f9f 100644 --- a/src/server/game/Entities/Object/Updates/UpdateData.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateData.cpp @@ -18,18 +18,36 @@ #include "UpdateData.h" #include "Errors.h" #include "WorldPacket.h" -#include "Opcodes.h" +#include <utility> UpdateData::UpdateData(uint32 map) : m_map(map), m_blockCount(0) { } -void UpdateData::AddDestroyObject(ObjectGuid guid) +UpdateData::UpdateData(UpdateData&& right) noexcept : + m_map(right.m_map), m_blockCount(std::exchange(right.m_blockCount, 0)), + m_destroyGUIDs(std::move(right.m_destroyGUIDs)), + m_outOfRangeGUIDs(std::move(right.m_outOfRangeGUIDs)), + m_data(std::move(right.m_data)) { - m_destroyGUIDs.insert(guid); } -void UpdateData::AddOutOfRangeGUID(GuidSet& guids) +UpdateData& UpdateData::operator=(UpdateData&& right) noexcept +{ + if (this != &right) + { + m_map = right.m_map; + m_blockCount = std::exchange(right.m_blockCount, 0); + m_destroyGUIDs = std::move(right.m_destroyGUIDs); + m_outOfRangeGUIDs = std::move(right.m_outOfRangeGUIDs); + m_data = std::move(right.m_data); + } + return *this; +} + +UpdateData::~UpdateData() = default; + +void UpdateData::AddDestroyObject(ObjectGuid guid) { - m_outOfRangeGUIDs.insert(guids.begin(), guids.end()); + m_destroyGUIDs.insert(guid); } void UpdateData::AddOutOfRangeGUID(ObjectGuid guid) diff --git a/src/server/game/Entities/Object/Updates/UpdateData.h b/src/server/game/Entities/Object/Updates/UpdateData.h index 3dc50bde23d..48ad58659e5 100644 --- a/src/server/game/Entities/Object/Updates/UpdateData.h +++ b/src/server/game/Entities/Object/Updates/UpdateData.h @@ -15,13 +15,13 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __UPDATEDATA_H -#define __UPDATEDATA_H +#ifndef TRINITYCORE_UPDATE_DATA_H +#define TRINITYCORE_UPDATE_DATA_H -#include "Define.h" #include "ByteBuffer.h" +#include "Define.h" +#include "FlatSet.h" #include "ObjectGuid.h" -#include <set> class WorldPacket; @@ -37,14 +37,11 @@ class UpdateData { public: UpdateData(uint32 map); - UpdateData(UpdateData&& right) noexcept : m_map(right.m_map), m_blockCount(right.m_blockCount), - m_outOfRangeGUIDs(std::move(right.m_outOfRangeGUIDs)), - m_data(std::move(right.m_data)) - { - } + UpdateData(UpdateData&& right) noexcept; + UpdateData& operator=(UpdateData&& right) noexcept; + ~UpdateData(); void AddDestroyObject(ObjectGuid guid); - void AddOutOfRangeGUID(GuidSet& guids); void AddOutOfRangeGUID(ObjectGuid guid); void AddUpdateBlock() { ++m_blockCount; } ByteBuffer& GetBuffer() { return m_data; } @@ -52,16 +49,15 @@ class UpdateData bool HasData() const { return m_blockCount > 0 || !m_outOfRangeGUIDs.empty() || !m_destroyGUIDs.empty(); } void Clear(); - GuidSet const& GetOutOfRangeGUIDs() const { return m_outOfRangeGUIDs; } - protected: uint32 m_map; uint32 m_blockCount; - GuidSet m_destroyGUIDs; - GuidSet m_outOfRangeGUIDs; + Trinity::Containers::FlatSet<ObjectGuid> m_destroyGUIDs; + Trinity::Containers::FlatSet<ObjectGuid> m_outOfRangeGUIDs; ByteBuffer m_data; UpdateData(UpdateData const& right) = delete; UpdateData& operator=(UpdateData const& right) = delete; }; -#endif + +#endif // TRINITYCORE_UPDATE_DATA_H diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp index 7627a6a82ae..5f1e6013196 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp @@ -8478,13 +8478,13 @@ void ConversationData::ClearChangesMask() _changesMask.ResetAll(); } -void AaBox::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void AaBox::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { data << Low; data << High; } -void AaBox::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void AaBox::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { data << Low; data << High; @@ -8588,7 +8588,7 @@ void VendorData::ClearChangesMask() _changesMask.ResetAll(); } -void DecorStoragePersistedDataDyes::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void DecorStoragePersistedDataDyes::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { for (uint32 i = 0; i < 3; ++i) { @@ -8596,7 +8596,7 @@ void DecorStoragePersistedDataDyes::WriteCreate(ByteBuffer& data, Object const* } } -void DecorStoragePersistedDataDyes::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void DecorStoragePersistedDataDyes::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { for (uint32 i = 0; i < 3; ++i) { @@ -8606,10 +8606,10 @@ void DecorStoragePersistedDataDyes::WriteUpdate(ByteBuffer& data, bool ignoreCha bool DecorStoragePersistedDataDyes::operator==(DecorStoragePersistedDataDyes const& right) const { - return std::ranges::equal(DyeColorID, right.DyeColorID); + return DyeColorID == right.DyeColorID; } -void DecorStoragePersistedData::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void DecorStoragePersistedData::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { data << *HouseGUID; data << uint8(Field_20); @@ -8621,7 +8621,7 @@ void DecorStoragePersistedData::WriteCreate(ByteBuffer& data, Object const* owne } } -void DecorStoragePersistedData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void DecorStoragePersistedData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { Mask changesMask = _changesMask; if (ignoreChangesMask) @@ -8721,7 +8721,7 @@ void HousingDecorData::ClearChangesMask() _changesMask.ResetAll(); } -void HousingDoorData::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void HousingDoorData::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { data << int32(RoomComponentID); data << *RoomComponentOffset; @@ -8729,7 +8729,7 @@ void HousingDoorData::WriteCreate(ByteBuffer& data, Object const* owner, Player data << *AttachedRoomGUID; } -void HousingDoorData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void HousingDoorData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { Mask changesMask = _changesMask; if (ignoreChangesMask) @@ -8768,7 +8768,7 @@ void HousingDoorData::ClearChangesMask() _changesMask.ResetAll(); } -void HousingRoomData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void HousingRoomData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { data << *HouseGUID; data << int32(HouseRoomID); @@ -8786,12 +8786,12 @@ void HousingRoomData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fi } } -void HousingRoomData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void HousingRoomData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { WriteUpdate(data, _changesMask, false, owner, receiver); } -void HousingRoomData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const +void HousingRoomData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const { data.WriteBits(changesMask.GetBlock(0), 7); @@ -8943,7 +8943,7 @@ void HousingRoomComponentMeshData::ClearChangesMask() _changesMask.ResetAll(); } -void HousingPlayerHouseData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void HousingPlayerHouseData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { data << *BnetAccount; data << int32(PlotIndex); @@ -8956,12 +8956,12 @@ void HousingPlayerHouseData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldF data << *EntityGUID; } -void HousingPlayerHouseData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void HousingPlayerHouseData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { WriteUpdate(data, _changesMask, false, owner, receiver); } -void HousingPlayerHouseData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const +void HousingPlayerHouseData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const { data.WriteBits(changesMask.GetBlock(0), 10); @@ -9105,13 +9105,13 @@ void HousingPlotAreaTriggerData::ClearChangesMask() _changesMask.ResetAll(); } -void PlayerHouseInfo::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void PlayerHouseInfo::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { data << HouseGUID; data << OwnerGUID; } -void PlayerHouseInfo::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void PlayerHouseInfo::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { data << HouseGUID; data << OwnerGUID; @@ -9123,13 +9123,13 @@ bool PlayerHouseInfo::operator==(PlayerHouseInfo const& right) const && OwnerGUID == right.OwnerGUID; } -void HousingOwner::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void HousingOwner::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { data << BnetAccountGUID; data << PlayerGUID; } -void HousingOwner::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void HousingOwner::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { data << BnetAccountGUID; data << PlayerGUID; @@ -9141,7 +9141,7 @@ bool HousingOwner::operator==(HousingOwner const& right) const && PlayerGUID == right.PlayerGUID; } -void NeighborhoodMirrorData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void NeighborhoodMirrorData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { data.WriteBits(Name->size() + 1, 8); data << *OwnerGUID; @@ -9158,12 +9158,12 @@ void NeighborhoodMirrorData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldF } } -void NeighborhoodMirrorData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void NeighborhoodMirrorData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { WriteUpdate(data, _changesMask, false, owner, receiver); } -void NeighborhoodMirrorData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const +void NeighborhoodMirrorData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const { data.WriteBits(changesMask.GetBlock(0), 5); @@ -9235,7 +9235,7 @@ void NeighborhoodMirrorData::ClearChangesMask() _changesMask.ResetAll(); } -void MirroredMeshObjectData::WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const +void MirroredMeshObjectData::WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const { data << *AttachParentGUID; data << *PositionLocalSpace; @@ -9247,7 +9247,7 @@ void MirroredMeshObjectData::WriteCreate(ByteBuffer& data, Object const* owner, data << uint8(AttachmentFlags); } -void MirroredMeshObjectData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const +void MirroredMeshObjectData::WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const { Mask changesMask = _changesMask; if (ignoreChangesMask) @@ -9294,17 +9294,17 @@ void MirroredMeshObjectData::ClearChangesMask() _changesMask.ResetAll(); } -void MirroredPositionData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void MirroredPositionData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { PositionData->WriteCreate(data, owner, receiver); } -void MirroredPositionData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void MirroredPositionData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { WriteUpdate(data, _changesMask, false, owner, receiver); } -void MirroredPositionData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const +void MirroredPositionData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const { data.WriteBits(changesMask.GetBlock(0), 2); @@ -9590,18 +9590,18 @@ void PlayerHouseInfoComponentData::ClearChangesMask() _changesMask.ResetAll(); } -void HousingStorageData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void HousingStorageData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { WriteMapFieldCreate(Decor, data, owner, receiver); data << uint32(DecorMaxOwnedCount); } -void HousingStorageData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const +void HousingStorageData::WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const { WriteUpdate(data, _changesMask, false, owner, receiver); } -void HousingStorageData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const +void HousingStorageData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const { data.WriteBits(changesMask.GetBlock(0), 3); diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h index 582473c702c..95976b87218 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -15,8 +15,8 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef UpdateFields_h__ -#define UpdateFields_h__ +#ifndef TRINITYCORE_UPDATE_FIELDS_H +#define TRINITYCORE_UPDATE_FIELDS_H #include "EnumFlag.h" #include "ItemPacketsCommon.h" @@ -34,6 +34,7 @@ class AreaTrigger; class AzeriteEmpoweredItem; class AzeriteItem; class Bag; +class BaseEntity; class ByteBuffer; class Conversation; class Corpse; @@ -1610,8 +1611,8 @@ struct AaBox : public IsUpdateFieldStructureTag TaggedPosition<Position::XYZ> Low; TaggedPosition<Position::XYZ> High; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; bool operator==(AaBox const& right) const; bool operator!=(AaBox const& right) const { return !(*this == right); } }; @@ -1641,10 +1642,10 @@ struct VendorData : public IsUpdateFieldStructureTag, public HasChangesMask<2> struct DecorStoragePersistedDataDyes : public IsUpdateFieldStructureTag { - int32 DyeColorID[3]; + std::array<int32, 3> DyeColorID; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; bool operator==(DecorStoragePersistedDataDyes const& right) const; bool operator!=(DecorStoragePersistedDataDyes const& right) const { return !(*this == right); } }; @@ -1655,8 +1656,8 @@ struct DecorStoragePersistedData : public IsUpdateFieldStructureTag, public HasC OptionalUpdateField<UF::DecorStoragePersistedDataDyes, -1, 1> Dyes; UpdateField<uint8, -1, 2> Field_20; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1681,8 +1682,8 @@ struct HousingDoorData : public IsUpdateFieldStructureTag, public HasChangesMask UpdateField<uint8, 0, 3> RoomComponentType; UpdateField<ObjectGuid, 0, 4> AttachedRoomGUID; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1695,9 +1696,9 @@ struct HousingRoomData : public IsUpdateFieldStructureTag, public HasChangesMask UpdateField<int32, 0, 5> Flags; UpdateField<int32, 0, 6> FloorIndex; - void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1731,9 +1732,9 @@ struct HousingPlayerHouseData : public IsUpdateFieldStructureTag, public HasChan UpdateField<uint32, 0, 8> RoomPlacementBudget; UpdateField<ObjectGuid, 0, 9> EntityGUID; - void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1766,8 +1767,8 @@ struct PlayerHouseInfo : public IsUpdateFieldStructureTag ObjectGuid HouseGUID; ObjectGuid OwnerGUID; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; bool operator==(PlayerHouseInfo const& right) const; bool operator!=(PlayerHouseInfo const& right) const { return !(*this == right); } }; @@ -1777,8 +1778,8 @@ struct HousingOwner : public IsUpdateFieldStructureTag ObjectGuid BnetAccountGUID; ObjectGuid PlayerGUID; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; bool operator==(HousingOwner const& right) const; bool operator!=(HousingOwner const& right) const { return !(*this == right); } }; @@ -1790,9 +1791,9 @@ struct NeighborhoodMirrorData : public IsUpdateFieldStructureTag, public HasChan UpdateField<ObjectGuid, 0, 4> OwnerGUID; UpdateField<std::string, 0, 3> Name; - void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1804,8 +1805,8 @@ struct MirroredMeshObjectData : public IsUpdateFieldStructureTag, public HasChan UpdateField<float, 0, 4> ScaleLocalSpace; UpdateField<uint8, 0, 5> AttachmentFlags; - void WriteCreate(ByteBuffer& data, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, bool ignoreChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1813,9 +1814,9 @@ struct MirroredPositionData : public IsUpdateFieldStructureTag, public HasChange { UpdateField<UF::MirroredMeshObjectData, 0, 1> PositionData; - void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1880,9 +1881,9 @@ struct HousingStorageData : public IsUpdateFieldStructureTag, public HasChangesM MapUpdateField<ObjectGuid, UF::DecorStoragePersistedData, 0, 1> Decor; UpdateField<uint32, 0, 2> DecorMaxOwnedCount; - void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, Object const* owner, Player const* receiver) const; - void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, Object const* owner, Player const* receiver) const; + void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, BaseEntity const* owner, Player const* receiver) const; + void WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignoreNestedChangesMask, BaseEntity const* owner, Player const* receiver) const; void ClearChangesMask(); }; @@ -1907,4 +1908,4 @@ struct HousingFixtureData : public IsUpdateFieldStructureTag, public HasChangesM } -#endif // UpdateFields_h__ +#endif // TRINITYCORE_UPDATE_FIELDS_H diff --git a/src/server/game/Entities/Object/Updates/UpdateMask.h b/src/server/game/Entities/Object/Updates/UpdateMask.h index 3071632d603..0e3fd9f32ae 100644 --- a/src/server/game/Entities/Object/Updates/UpdateMask.h +++ b/src/server/game/Entities/Object/Updates/UpdateMask.h @@ -19,7 +19,6 @@ #define UpdateMask_h__ #include "Define.h" -#include <algorithm> #include <cstring> // std::memset namespace UpdateMaskHelpers @@ -68,10 +67,11 @@ public: constexpr bool IsAnySet() const { - return std::ranges::any_of(_blocksMask, [](uint32 blockMask) - { - return blockMask != 0; - }); + for (uint32 i = 0; i < BlocksMaskCount; ++i) + if (_blocksMask[i]) + return true; + + return false; } constexpr void Reset(uint32 index) diff --git a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp index b89d7aab275..b80541d547d 100644 --- a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp +++ b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp @@ -21,7 +21,8 @@ namespace WowCS { -void EntityFragmentsHolder::Add(EntityFragment fragment, bool update) +void EntityFragmentsHolder::Add(EntityFragment fragment, bool update, + EntityFragmentSerializeFn serializeCreate, EntityFragmentSerializeFn serializeUpdate, EntityFragmentIsChangedFn isChanged) { ASSERT(Count < Ids.size()); @@ -43,23 +44,34 @@ void EntityFragmentsHolder::Add(EntityFragment fragment, bool update) if (IsUpdateableFragment(fragment)) { - ASSERT(UpdateableCount < UpdateableIds.size()); + ASSERT(UpdateableCount < Updateable.Ids.size()); + ASSERT(serializeCreate && serializeUpdate && isChanged); - auto insertedItr = insertSorted(UpdateableIds, UpdateableCount, fragment).first; - std::ptrdiff_t index = std::ranges::distance(UpdateableIds.begin(), insertedItr); + auto insertedItr = insertSorted(Updateable.Ids, UpdateableCount, fragment).first; + std::ptrdiff_t index = std::ranges::distance(Updateable.Ids.begin(), insertedItr); uint8 maskLowPart = ContentsChangedMask & ((1 << index) - 1); uint8 maskHighPart = (ContentsChangedMask & ~((1 << index) - 1)) << (1 + IsIndirectFragment(fragment)); ContentsChangedMask = maskLowPart | maskHighPart; for (uint8 i = 0, maskIndex = 0; i < UpdateableCount; ++i) { - UpdateableMasks[i] = 1 << maskIndex++; - if (IsIndirectFragment(UpdateableIds[i])) + Updateable.Masks[i] = 1 << maskIndex++; + if (IsIndirectFragment(Updateable.Ids[i])) { - ContentsChangedMask |= UpdateableMasks[i]; // set the first bit to true to activate fragment + ContentsChangedMask |= Updateable.Masks[i]; // set the first bit to true to activate fragment ++maskIndex; - UpdateableMasks[i] <<= 1; + Updateable.Masks[i] <<= 1; } } + + auto insertAtIndex = []<typename T, size_t N>(std::array<T, N>& arr, uint8 size, std::ptrdiff_t i, T value) + { + std::ranges::move_backward(arr.begin() + i, arr.begin() + size - 1, arr.begin() + size); + arr[i] = value; + }; + + insertAtIndex(Updateable.SerializeCreate, UpdateableCount, index, serializeCreate); + insertAtIndex(Updateable.SerializeUpdate, UpdateableCount, index, serializeUpdate); + insertAtIndex(Updateable.IsChanged, UpdateableCount, index, isChanged); } if (update) @@ -86,22 +98,32 @@ void EntityFragmentsHolder::Remove(EntityFragment fragment) if (IsUpdateableFragment(fragment)) { - auto [removedItr, removed] = removeSorted(UpdateableIds, UpdateableCount, fragment); + auto [removedItr, removed] = removeSorted(Updateable.Ids, UpdateableCount, fragment); if (removed) { - std::ptrdiff_t index = std::ranges::distance(UpdateableIds.begin(), removedItr); + std::ptrdiff_t index = std::ranges::distance(Updateable.Ids.begin(), removedItr); uint8 maskLowPart = ContentsChangedMask & ((1 << index) - 1); uint8 maskHighPart = (ContentsChangedMask & ~((1 << index) - 1)) >> (1 + IsIndirectFragment(fragment)); ContentsChangedMask = maskLowPart | maskHighPart; for (uint8 i = 0, maskIndex = 0; i < UpdateableCount; ++i) { - UpdateableMasks[i] = 1 << maskIndex++; - if (IsIndirectFragment(UpdateableIds[i])) + Updateable.Masks[i] = 1 << maskIndex++; + if (IsIndirectFragment(Updateable.Ids[i])) { ++maskIndex; - UpdateableMasks[i] <<= 1; + Updateable.Masks[i] <<= 1; } } + + auto removeAtIndex = []<typename T, size_t N>(std::array<T, N>& arr, uint8 oldSize, std::ptrdiff_t i, std::type_identity_t<T> value) + { + *std::ranges::move(arr.begin() + i + 1, arr.begin() + oldSize, arr.begin() + i).out = value; + }; + + uint8 oldSize = UpdateableCount + 1; + removeAtIndex(Updateable.SerializeCreate, oldSize, index, nullptr); + removeAtIndex(Updateable.SerializeUpdate, oldSize, index, nullptr); + removeAtIndex(Updateable.IsChanged, oldSize, index, nullptr); } } diff --git a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h index f3d8182ff83..d8e501e20ba 100644 --- a/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h +++ b/src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h @@ -22,6 +22,15 @@ #include <array> #include <span> +class BaseEntity; +class ByteBuffer; +class Player; + +namespace UF +{ +enum class UpdateFieldFlag : uint8; +} + namespace WowCS { enum class EntityFragment : uint8 @@ -99,10 +108,35 @@ inline constexpr bool IsIndirectFragment(EntityFragment frag) || frag == EntityFragment::PlayerHouseInfoComponent_C; } -// common case optimization, make use of the fact that fragment arrays are sorted -inline constexpr uint8 CGObjectActiveMask = 0x1; -inline constexpr uint8 CGObjectChangedMask = 0x2; -inline constexpr uint8 CGObjectUpdateMask = CGObjectActiveMask | CGObjectChangedMask; +template <auto /*FragmentMemberPtr*/> +struct FragmentSerializationTraits +{ +}; + +template <typename Entity, typename Fragment, Fragment Entity::* FragmentData> +struct FragmentSerializationTraits<FragmentData> +{ + static void BuildCreate(BaseEntity const* baseEntity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) + { + Entity const* entity = static_cast<Entity const*>(baseEntity); + (entity->*FragmentData)->WriteCreate(data, flags, entity, target); + } + + static void BuildUpdate(BaseEntity const* baseEntity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target) + { + Entity const* entity = static_cast<Entity const*>(baseEntity); + (entity->*FragmentData)->WriteUpdate(data, flags, entity, target); + } + + static bool IsChanged(BaseEntity const* baseEntity) + { + Entity const* entity = static_cast<Entity const*>(baseEntity); + return (entity->*FragmentData)->GetChangesMask().IsAnySet(); + } +}; + +using EntityFragmentSerializeFn = void (*)(BaseEntity const* entity, ByteBuffer& data, UF::UpdateFieldFlag flags, Player const* target); +using EntityFragmentIsChangedFn = bool (*)(BaseEntity const* entity); struct EntityFragmentsHolder { @@ -111,34 +145,42 @@ struct EntityFragmentsHolder EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End }; - uint8 Count = 0; - bool IdsChanged = false; - std::array<EntityFragment, 4> UpdateableIds = + template <std::size_t N> + struct UpdateableFragments { - EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End + std::array<EntityFragment, N> Ids = + { + EntityFragment::End, EntityFragment::End, EntityFragment::End, EntityFragment::End + }; + std::array<uint8, N> Masks = { }; + std::array<EntityFragmentSerializeFn, N> SerializeCreate = { }; + std::array<EntityFragmentSerializeFn, N> SerializeUpdate = { }; + std::array<EntityFragmentIsChangedFn, N> IsChanged = { }; }; - std::array<uint8, 4> UpdateableMasks = { }; + + UpdateableFragments<4> Updateable; + + uint8 Count = 0; + bool IdsChanged = false; + uint8 UpdateableCount = 0; uint8 ContentsChangedMask = 0; - void Add(EntityFragment fragment, bool update); - void Remove(EntityFragment fragment); + void Add(EntityFragment fragment, bool update, + EntityFragmentSerializeFn serializeCreate, EntityFragmentSerializeFn serializeUpdate, EntityFragmentIsChangedFn isChanged); - std::span<EntityFragment const> GetIds() const { return std::span(Ids.begin(), Count); } - std::span<EntityFragment const> GetUpdateableIds() const { return std::span(UpdateableIds.begin(), UpdateableCount); } + inline void Add(EntityFragment fragment, bool update) { Add(fragment, update, nullptr, nullptr, nullptr); } - uint8 GetUpdateMaskFor(EntityFragment fragment) const + template <typename SerializationTraits> + inline void Add(EntityFragment fragment, bool update, SerializationTraits) { - if (fragment == EntityFragment::CGObject) // common case optimization, make use of the fact that fragment arrays are sorted - return CGObjectChangedMask; + Add(fragment, update, &SerializationTraits::BuildCreate, &SerializationTraits::BuildUpdate, &SerializationTraits::IsChanged); + } - for (uint8 i = 1; i < UpdateableCount; ++i) - if (UpdateableIds[i] == fragment) - return UpdateableMasks[i]; + void Remove(EntityFragment fragment); - return 0; - } + std::span<EntityFragment const> GetIds() const { return std::span(Ids.begin(), Count); } }; enum class EntityFragmentSerializationType : uint8 diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 949743f16ae..669fcb36287 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1683,7 +1683,7 @@ bool Pet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 Entry, uint32 /*p SetMap(map); // TODO: counter should be constructed as (summon_count << 32) | petNumber - Object::_Create(ObjectGuid::Create<HighGuid::Pet>(map->GetId(), Entry, guidlow)); + _Create(ObjectGuid::Create<HighGuid::Pet>(map->GetId(), Entry, guidlow)); m_spawnId = guidlow; m_originalEntry = Entry; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ecddafd9c28..4eb409c8b12 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -17,6 +17,7 @@ #include "Player.h" #include "AreaTrigger.h" +#include "Account.h" #include "AccountMgr.h" #include "AchievementMgr.h" #include "ArenaTeam.h" @@ -387,7 +388,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac //FIXME: outfitId not used in player creating /// @todo need more checks against packet modifications - Object::_Create(ObjectGuid::Create<HighGuid::Player>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Player>(guidlow)); m_name = createInfo->Name; @@ -1540,6 +1541,8 @@ void Player::AddToWorld() for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) if (m_items[i]) m_items[i]->AddToWorld(); + + GetSession()->GetBattlenetAccount().AddToWorld(); } void Player::RemoveFromWorld() @@ -1559,6 +1562,8 @@ void Player::RemoveFromWorld() sBattlefieldMgr->HandlePlayerLeaveZone(this, m_zoneUpdateId); } + GetSession()->GetBattlenetAccount().RemoveFromWorld(); + // Remove items from world before self - player must be found in Item::RemoveFromObjectUpdate for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i) if (m_items[i]) @@ -3592,6 +3597,8 @@ void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c for (Item* item : m_items) if (item) item->BuildCreateUpdateBlockForPlayer(data, target); + + GetSession()->GetBattlenetAccount().BuildCreateUpdateBlockForPlayer(data, target); } Unit::BuildCreateUpdateBlockForPlayer(data, target); @@ -3706,7 +3713,7 @@ void Player::ValuesUpdateForPlayerWithMaskSender::operator()(Player const* playe player->SendDirectMessage(&packet); } -void Player::DestroyForPlayer(Player* target) const +void Player::DestroyForPlayer(Player const* target) const { Unit::DestroyForPlayer(target); @@ -17856,7 +17863,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol return false; } - Object::_Create(guid); + _Create(guid); m_name = std::move(fields.name); @@ -24097,7 +24104,7 @@ uint8 Player::GetStartLevel(uint8 race, uint8 playerClass, Optional<int32> chara return startLevel; } -bool Player::HaveAtClient(Object const* u) const +bool Player::HaveAtClient(BaseEntity const* u) const { return u == this || m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end(); } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 62b5e410559..ead0c144f7f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2233,7 +2233,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void operator()(Player const* player) const; }; - void DestroyForPlayer(Player* target) const override; + void DestroyForPlayer(Player const* target) const override; // notifiers void SendAttackSwingCancelAttack() const; @@ -2631,7 +2631,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> GuidUnorderedSet m_clientGUIDs; GuidUnorderedSet m_visibleTransports; - bool HaveAtClient(Object const* u) const; + bool HaveAtClient(BaseEntity const* u) const; bool IsNeverVisibleFor(WorldObject const* seer, bool allowServersideObjects = false) const override; diff --git a/src/server/game/Entities/SceneObject/SceneObject.cpp b/src/server/game/Entities/SceneObject/SceneObject.cpp index 46875c51b06..3d8a7bca451 100644 --- a/src/server/game/Entities/SceneObject/SceneObject.cpp +++ b/src/server/game/Entities/SceneObject/SceneObject.cpp @@ -117,7 +117,7 @@ bool SceneObject::Create(ObjectGuid::LowType lowGuid, SceneType type, uint32 sce SetPrivateObjectOwner(privateObjectOwner); - Object::_Create(ObjectGuid::Create<HighGuid::SceneObject>(GetMapId(), sceneId, lowGuid)); + _Create(ObjectGuid::Create<HighGuid::SceneObject>(GetMapId(), sceneId, lowGuid)); PhasingHandler::InheritPhaseShift(this, creator); UpdatePositionData(); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index f7ac68ef840..81733d625fd 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -107,7 +107,7 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, float x, float return false; } - Object::_Create(ObjectGuid::Create<HighGuid::Transport>(guidlow)); + _Create(ObjectGuid::Create<HighGuid::Transport>(guidlow)); GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); if (!goinfo) @@ -689,6 +689,8 @@ void Transport::UpdatePassengerPositions(PassengerSet const& passengers) void Transport::BuildUpdate(UpdateDataMapType& data_map) { + BuildUpdateChangesMask(); + for (MapReference const& playerReference : GetMap()->GetPlayers()) if (playerReference.GetSource()->InSamePhase(this)) BuildFieldsUpdate(playerReference.GetSource(), data_map); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 9c5c0a2c134..f51da1de0f8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -53,6 +53,7 @@ #include "Loot.h" #include "LootMgr.h" #include "LootPackets.h" +#include "MapUtils.h" #include "MiscPackets.h" #include "MotionMaster.h" #include "MovementGenerator.h" @@ -312,7 +313,7 @@ Unit::Unit(bool isWorldObject) : m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr), i_motionMaster(std::make_unique<MotionMaster>(this)), m_regenTimer(0), m_vehicle(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this), - m_threatManager(this), m_aiLocked(false), _playHoverAnim(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0), + m_threatManager(this), m_aiLocked(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0), _spellHistory(std::make_unique<SpellHistory>(this)) { m_objectTypeId = TYPEID_UNIT; @@ -1597,7 +1598,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) SpellInfo const* spellInfo = aurEff->GetSpellInfo(); // Damage shield can be resisted... - SpellMissInfo missInfo = victim->SpellHitResult(this, spellInfo, false); + SpellMissInfo missInfo = victim->SpellHitResult(this, spellInfo, false, true); if (missInfo != SPELL_MISS_NONE) { victim->SendSpellMiss(this, spellInfo->Id, missInfo); @@ -5901,6 +5902,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) m_attacking = victim; m_attacking->_addAttacker(this); + m_updateFlag.CombatVictim = true; // Set our target SetTarget(victim->GetGUID()); @@ -5951,6 +5953,7 @@ bool Unit::AttackStop() Unit* victim = m_attacking; + m_updateFlag.CombatVictim = false; m_attacking->_removeAttacker(this); m_attacking = nullptr; @@ -7743,7 +7746,7 @@ int32 Unit::SpellAbsorbBonusTaken(Unit* caster, SpellInfo const* spellProto, int return static_cast<int32>(std::round(absorb)); } -bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute /*= false*/) const +bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute /*= false*/) const { if (!spellInfo) return false; @@ -7754,14 +7757,14 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste if (!requireImmunityPurgesEffectAttribute) return range.begin() != range.end(); - return std::any_of(range.begin(), range.end(), [](SpellImmuneContainer::value_type const& entry) + return std::ranges::any_of(range, [](uint32 immunitySpellId) { - if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(entry.second, DIFFICULTY_NONE)) + if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(immunitySpellId, DIFFICULTY_NONE)) if (immunitySourceSpell->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) return true; return false; - }); + }, Trinity::Containers::MapValue); }; // Single spell immunity. @@ -7792,7 +7795,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste { // State/effect immunities applied by aura expect full spell immunity // Ignore effects with mechanic, they are supposed to be checked separately - if (!spellEffectInfo.IsEffect()) + if (!spellEffectInfo.IsEffect() || !(effectMask & (1 << spellEffectInfo.EffectIndex))) continue; if (!IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute)) { @@ -7810,23 +7813,23 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste { uint32 schoolImmunityMask = 0; SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) + for (auto [auraSchoolImmunityMask, immunityAuraId] : schoolList) { - if ((itr->first & schoolMask) == 0) + if ((auraSchoolImmunityMask & schoolMask) == 0) continue; - SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second, GetMap()->GetDifficultyID()); + SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(immunityAuraId, GetMap()->GetDifficultyID()); if (requireImmunityPurgesEffectAttribute) if (!immuneSpellInfo || !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) continue; - if (!(immuneSpellInfo && immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)) && caster && caster->IsFriendlyTo(this)) + if (!(spellInfo->NegativeEffects.to_ulong() & effectMask) && !(immuneSpellInfo && immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))) continue; if (spellInfo->CanPierceImmuneAura(immuneSpellInfo)) continue; - schoolImmunityMask |= itr->first; + schoolImmunityMask |= auraSchoolImmunityMask; } if ((schoolImmunityMask & schoolMask) == schoolMask) return true; @@ -7893,7 +7896,7 @@ bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const return false; } -bool Unit::IsImmunedToDamage(WorldObject const* caster, SpellInfo const* spellInfo, SpellEffectInfo const* spellEffectInfo /*= nullptr*/) const +bool Unit::IsImmunedToDamage(WorldObject const* /*caster*/, SpellInfo const* spellInfo, SpellEffectInfo const* spellEffectInfo /*= nullptr*/) const { if (!spellInfo) return false; @@ -7912,7 +7915,7 @@ bool Unit::IsImmunedToDamage(WorldObject const* caster, SpellInfo const* spellIn for (auto&& [immunitySchoolMask, immunityAuraId] : container) { SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId, GetMap()->GetDifficultyID()); - if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this)) + if (spellInfo->IsPositive() && !(immuneAuraInfo && immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))) continue; if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo)) @@ -7937,7 +7940,7 @@ bool Unit::IsImmunedToDamage(WorldObject const* caster, SpellInfo const* spellIn return false; } -bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, +bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* /*caster*/, bool requireImmunityPurgesEffectAttribute /*= false*/) const { if (!spellInfo) @@ -7955,14 +7958,14 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo co if (!requireImmunityPurgesEffectAttribute) return range.begin() != range.end(); - return std::any_of(range.begin(), range.end(), [](SpellImmuneContainer::value_type const& entry) + return std::ranges::any_of(range, [](uint32 immunitySpellId) { - if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(entry.second, DIFFICULTY_NONE)) + if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(immunitySpellId, DIFFICULTY_NONE)) if (immunitySourceSpell->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) return true; return false; - }); + }, Trinity::Containers::MapValue); }; // If m_immuneToEffect type contain this effect type, IMMUNE effect. @@ -7994,7 +7997,10 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo co if (!(immuneAuraApply->GetMiscValue() & spellInfo->GetSchoolMask())) // Check school continue; - if (immuneAuraApply->GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || (caster && !IsFriendlyTo(caster))) // Harmful + if (!spellInfo->IsPositiveEffect(spellEffectInfo.EffectIndex)) // Harmful + return true; + + if (immuneAuraApply->GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)) // Friendly return true; } } @@ -8003,26 +8009,24 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo co return false; } -bool Unit::IsImmunedToAuraPeriodicTick(WorldObject const* caster, SpellInfo const* spellInfo, SpellEffectInfo const* spellEffectInfo) const +bool Unit::IsImmunedToAuraPeriodicTick(WorldObject const* /*caster*/, AuraEffect const* auraEffect) const { - if (!spellInfo) + if (auraEffect->GetSpellInfo()->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || auraEffect->GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) /*only school immunities are checked in this function*/) return false; - if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) /*only school immunities are checked in this function*/) + if (auraEffect->GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::NoImmunity)) return false; - if (spellEffectInfo && spellEffectInfo->EffectAttributes.HasFlag(SpellEffectAttributes::NoImmunity)) - return false; - - if (uint32 schoolMask = spellInfo->GetSchoolMask()) + if (uint32 schoolMask = auraEffect->GetSpellInfo()->GetSchoolMask()) { auto hasImmunity = [&](SpellImmuneContainer const& container) { + bool isPositive = auraEffect->GetBase()->GetApplicationOfTarget(GetGUID())->IsPositive(); uint32 schoolImmunityMask = 0; for (auto&& [immunitySchoolMask, immunityAuraId] : container) { SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId, GetMap()->GetDifficultyID()); - if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this)) + if (isPositive && !(immuneAuraInfo && immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))) continue; schoolImmunityMask |= immunitySchoolMask; @@ -11199,6 +11203,7 @@ void Unit::SetAIAnimKitId(uint16 animKitId) return; _aiAnimKitId = animKitId; + m_updateFlag.AnimKit = _aiAnimKitId != 0 || _movementAnimKitId != 0 || _meleeAnimKitId != 0; WorldPackets::Misc::SetAIAnimKit data; data.Unit = GetGUID(); @@ -11215,6 +11220,7 @@ void Unit::SetMovementAnimKitId(uint16 animKitId) return; _movementAnimKitId = animKitId; + m_updateFlag.AnimKit = _aiAnimKitId != 0 || _movementAnimKitId != 0 || _meleeAnimKitId != 0; WorldPackets::Misc::SetMovementAnimKit data; data.Unit = GetGUID(); @@ -11231,6 +11237,7 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId) return; _meleeAnimKitId = animKitId; + m_updateFlag.AnimKit = _aiAnimKitId != 0 || _movementAnimKitId != 0 || _meleeAnimKitId != 0; WorldPackets::Misc::SetMeleeAnimKit data; data.Unit = GetGUID(); @@ -12282,7 +12289,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target) if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_ALLOW_DEAD_TARGET)) return nullptr; - if (target->IsImmunedToSpell(spellInfo, this)) + if (target->IsImmunedToSpell(spellInfo, effMask, this)) return nullptr; for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects()) @@ -14072,7 +14079,7 @@ void Unit::SetPlayHoverAnim(bool enable, bool sendUpdate /*= true*/) if (IsPlayingHoverAnim() == enable) return; - _playHoverAnim = enable; + m_updateFlag.PlayHoverAnim = enable; if (!sendUpdate) return; @@ -14121,7 +14128,7 @@ UF::UpdateFieldFlag Unit::GetUpdateFieldFlagsFor(Player const* target) const return flags; } -void Unit::DestroyForPlayer(Player* target) const +void Unit::DestroyForPlayer(Player const* target) const { if (Battleground* bg = target->GetBattleground()) { diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index d6392f5b153..8964e1408d8 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1138,7 +1138,7 @@ class TC_GAME_API Unit : public WorldObject void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false); - bool IsPlayingHoverAnim() const { return _playHoverAnim; } + bool IsPlayingHoverAnim() const { return m_updateFlag.PlayHoverAnim; } void SetPlayHoverAnim(bool enable, bool sendUpdate = true); void CalculateHoverHeight(); void SetHoverHeight(float hoverHeight) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::HoverHeight), hoverHeight); } @@ -1677,7 +1677,7 @@ class TC_GAME_API Unit : public WorldObject static uint32 SpellCriticalHealingBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim); void ApplySpellImmune(uint32 spellId, SpellImmunity op, uint32 type, bool apply); - bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const; + bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const; uint32 GetSchoolImmunityMask() const; uint32 GetDamageImmunityMask() const; uint64 GetMechanicImmunityMask() const; @@ -1687,7 +1687,7 @@ class TC_GAME_API Unit : public WorldObject bool IsImmunedToDamage(WorldObject const* caster, SpellInfo const* spellInfo, SpellEffectInfo const* spellEffectInfo = nullptr) const; virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const; - bool IsImmunedToAuraPeriodicTick(WorldObject const* caster, SpellInfo const* spellInfo, SpellEffectInfo const* spellEffectInfo = nullptr) const; + bool IsImmunedToAuraPeriodicTick(WorldObject const* caster, AuraEffect const* auraEffect) const; static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = nullptr); static uint32 CalcArmorReducedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK, uint8 attackerLevel = 0); @@ -1812,14 +1812,14 @@ class TC_GAME_API Unit : public WorldObject virtual bool IsLoading() const { return false; } bool IsDuringRemoveFromWorld() const {return m_duringRemoveFromWorld;} - Pet* ToPet() { if (IsPet()) return reinterpret_cast<Pet*>(this); else return nullptr; } - Pet const* ToPet() const { if (IsPet()) return reinterpret_cast<Pet const*>(this); else return nullptr; } + Pet* ToPet() { return IsPet() ? reinterpret_cast<Pet*>(this) : nullptr; } + Pet const* ToPet() const { return IsPet() ? reinterpret_cast<Pet const*>(this) : nullptr; } - Totem* ToTotem() { if (IsTotem()) return reinterpret_cast<Totem*>(this); else return nullptr; } - Totem const* ToTotem() const { if (IsTotem()) return reinterpret_cast<Totem const*>(this); else return nullptr; } + Totem* ToTotem() { return IsTotem() ? reinterpret_cast<Totem*>(this) : nullptr; } + Totem const* ToTotem() const { return IsTotem() ? reinterpret_cast<Totem const*>(this) : nullptr; } - TempSummon* ToTempSummon() { if (IsSummon()) return reinterpret_cast<TempSummon*>(this); else return nullptr; } - TempSummon const* ToTempSummon() const { if (IsSummon()) return reinterpret_cast<TempSummon const*>(this); else return nullptr; } + TempSummon* ToTempSummon() { return IsSummon() ? reinterpret_cast<TempSummon*>(this) : nullptr; } + TempSummon const* ToTempSummon() const { return IsSummon() ? reinterpret_cast<TempSummon const*>(this) : nullptr; } ObjectGuid GetTarget() const { return m_unitData->Target; } virtual void SetTarget(ObjectGuid const& /*guid*/) = 0; @@ -1878,7 +1878,7 @@ class TC_GAME_API Unit : public WorldObject UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const override; - void DestroyForPlayer(Player* target) const override; + void DestroyForPlayer(Player const* target) const override; void ClearUpdateMask(bool remove) override; void _UpdateSpells(uint32 time); @@ -2030,8 +2030,6 @@ class TC_GAME_API Unit : public WorldObject uint32 _oldFactionId; ///< faction before charm bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed? - bool _playHoverAnim; - uint16 _aiAnimKitId; uint16 _movementAnimKitId; uint16 _meleeAnimKitId; diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 232c5748d51..ca39f80ec34 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -34,6 +34,18 @@ #include "Unit.h" #include <sstream> +class VehicleJoinEvent : public BasicEvent +{ +public: + VehicleJoinEvent(Vehicle* v, Unit* u) : Target(v), Passenger(u), Seat(Target->Seats.end()) { } + bool Execute(uint64, uint32) override; + void Abort(uint64) override; + + Vehicle* Target; + Unit* Passenger; + SeatMap::iterator Seat; +}; + Vehicle::Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry) : UsableSeatNum(0), _me(unit), _vehicleInfo(vehInfo), _creatureEntry(creatureEntry), _status(STATUS_NONE) { diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index fb740a7d962..f494f0684b3 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -122,17 +122,4 @@ class TC_GAME_API Vehicle final : public TransportBase PendingJoinEventContainer _pendingJoinEvents; ///< Collection of delayed join events for prospective passengers }; -class TC_GAME_API VehicleJoinEvent : public BasicEvent -{ - friend class Vehicle; - protected: - VehicleJoinEvent(Vehicle* v, Unit* u) : Target(v), Passenger(u), Seat(Target->Seats.end()) { } - bool Execute(uint64, uint32) override; - void Abort(uint64) override; - - Vehicle* Target; - Unit* Passenger; - SeatMap::iterator Seat; -}; - #endif diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index c886f41ae44..0ea2ff54b80 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1967,7 +1967,7 @@ void Map::SendObjectUpdates() while (!_updateObjects.empty()) { - Object* obj = *_updateObjects.begin(); + BaseEntity* obj = *_updateObjects.begin(); ASSERT(obj->IsInWorld()); _updateObjects.erase(_updateObjects.begin()); obj->BuildUpdate(update_players); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index c3fcfeb1aa4..6c5147727d3 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -45,6 +45,7 @@ #include <set> #include <unordered_set> +class BaseEntity; class Battleground; class BattlegroundMap; class BattlegroundScript; @@ -486,11 +487,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType> return nullptr; } - InstanceMap* ToInstanceMap() { if (IsDungeon()) return reinterpret_cast<InstanceMap*>(this); else return nullptr; } - InstanceMap const* ToInstanceMap() const { if (IsDungeon()) return reinterpret_cast<InstanceMap const*>(this); return nullptr; } + InstanceMap* ToInstanceMap() { return IsDungeon() ? reinterpret_cast<InstanceMap*>(this) : nullptr; } + InstanceMap const* ToInstanceMap() const { return IsDungeon() ? reinterpret_cast<InstanceMap const*>(this) : nullptr; } - BattlegroundMap* ToBattlegroundMap() { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap*>(this); else return nullptr; } - BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; } + BattlegroundMap* ToBattlegroundMap() { return IsBattlegroundOrArena() ? reinterpret_cast<BattlegroundMap*>(this) : nullptr; } + BattlegroundMap const* ToBattlegroundMap() const { return IsBattlegroundOrArena() ? reinterpret_cast<BattlegroundMap const*>(this) : nullptr; } bool isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const; void Balance() { _dynamicTree.balance(); } @@ -567,12 +568,12 @@ class TC_GAME_API Map : public GridRefManager<NGridType> return GetGuidSequenceGenerator(high).GetNextAfterMaxUsed(); } - void AddUpdateObject(Object* obj) + void AddUpdateObject(BaseEntity* obj) { _updateObjects.insert(obj); } - void RemoveUpdateObject(Object* obj) + void RemoveUpdateObject(BaseEntity* obj) { _updateObjects.erase(obj); } @@ -821,7 +822,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType> std::unordered_map<ObjectGuid, Corpse*> _corpsesByPlayer; std::unordered_set<Corpse*> _corpseBones; - std::unordered_set<Object*> _updateObjects; + std::unordered_set<BaseEntity*> _updateObjects; MPSCQueue<FarSpellCallback> _farSpellCallbacks; diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index af61fd0a27f..d33e418c51f 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -593,16 +593,15 @@ void MotionMaster::MoveTargetedHome() } } -void MotionMaster::MoveRandom(float wanderDistance /*= 0.0f*/, Optional<Milliseconds> duration /*= {}*/, MovementSlot slot /*= MOTION_SLOT_DEFAULT*/, +void MotionMaster::MoveRandom(float wanderDistance /*= 0.0f*/, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/, + MovementWalkRunSpeedSelectionMode speedSelectionMode /*= MovementWalkRunSpeedSelectionMode::ForceWalk*/, MovementSlot slot /*= MOTION_SLOT_DEFAULT*/, Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult /*= {}*/) { + TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveRandom: '{}', started random movement (spawnDist: {})", _owner->GetGUID(), wanderDistance); if (_owner->GetTypeId() == TYPEID_UNIT) - { - TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MoveRandom: '{}', started random movement (spawnDist: {})", _owner->GetGUID(), wanderDistance); - Add(new RandomMovementGenerator<Creature>(wanderDistance, duration, std::move(scriptResult)), slot); - } - else if (scriptResult) - scriptResult->SetResult(MovementStopReason::Interrupted); + Add(new RandomMovementGenerator<Creature>(wanderDistance, duration, speed, speedSelectionMode, std::move(scriptResult)), slot); + else + Add(new RandomMovementGenerator<Player>(wanderDistance, duration, speed, speedSelectionMode, std::move(scriptResult)), slot); } void MotionMaster::MoveFollow(Unit* target, float dist, Optional<ChaseAngle> angle /*= {}*/, Optional<Milliseconds> duration /*= {}*/, bool ignoreTargetWalk /*= false*/, MovementSlot slot/* = MOTION_SLOT_ACTIVE*/, diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 1126b6a5cab..d8696cbb44a 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -157,7 +157,8 @@ class TC_GAME_API MotionMaster void MoveIdle(); void MoveTargetedHome(); - void MoveRandom(float wanderDistance = 0.0f, Optional<Milliseconds> duration = {}, MovementSlot slot = MOTION_SLOT_DEFAULT, + void MoveRandom(float wanderDistance = 0.0f, Optional<Milliseconds> duration = {}, Optional<float> speed = {}, + MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode::ForceWalk, MovementSlot slot = MOTION_SLOT_DEFAULT, Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult = {}); void MoveFollow(Unit* target, float dist, Optional<ChaseAngle> angle = {}, Optional<Milliseconds> duration = {}, bool ignoreTargetWalk = false, MovementSlot slot = MOTION_SLOT_ACTIVE, Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult = {}); diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp index 64fbb953619..5f48c5c66e6 100755 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp @@ -42,10 +42,10 @@ MovementGeneratorType ConfusedMovementGenerator<T>::GetMovementGeneratorType() c template<class T> void ConfusedMovementGenerator<T>::DoInitialize(T* owner) { - MovementGenerator::RemoveFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); - MovementGenerator::AddFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED); - if (!owner || !owner->IsAlive()) + if (!owner->IsAlive()) return; // TODO: UNIT_FIELD_FLAGS should not be handled by generators @@ -60,7 +60,7 @@ void ConfusedMovementGenerator<T>::DoInitialize(T* owner) template<class T> void ConfusedMovementGenerator<T>::DoReset(T* owner) { - MovementGenerator::RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); DoInitialize(owner); } @@ -68,24 +68,24 @@ void ConfusedMovementGenerator<T>::DoReset(T* owner) template<class T> bool ConfusedMovementGenerator<T>::DoUpdate(T* owner, uint32 diff) { - if (!owner || !owner->IsAlive()) + if (!owner->IsAlive()) return false; if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - MovementGenerator::AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); owner->StopMoving(); _path = nullptr; return true; } else - MovementGenerator::RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); // waiting for next move _timer.Update(diff); - if ((MovementGenerator::HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING) && !owner->movespline->Finalized()) || (_timer.Passed() && owner->movespline->Finalized())) + if ((this->HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING) && !owner->movespline->Finalized()) || (_timer.Passed() && owner->movespline->Finalized())) { - MovementGenerator::RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY); Position destination(_reference); float distance = 4.0f * frand(0.0f, 1.0f) - 2.0f; @@ -130,48 +130,29 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T* owner, uint32 diff) template<class T> void ConfusedMovementGenerator<T>::DoDeactivate(T* owner) { - MovementGenerator::AddFlag(MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_DEACTIVATED); owner->ClearUnitState(UNIT_STATE_CONFUSED_MOVE); } template<class T> -void ConfusedMovementGenerator<T>::DoFinalize(T*, bool, bool) { } - -template<> -void ConfusedMovementGenerator<Player>::DoFinalize(Player* owner, bool active, bool/* movementInform*/) +void ConfusedMovementGenerator<T>::DoFinalize(T* owner, bool active, bool/* movementInform*/) { - AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); - - if (active) - { - owner->RemoveUnitFlag(UNIT_FLAG_CONFUSED); - owner->StopMoving(); - } -} - -template<> -void ConfusedMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, bool/* movementInform*/) -{ - AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); if (active) { owner->RemoveUnitFlag(UNIT_FLAG_CONFUSED); owner->ClearUnitState(UNIT_STATE_CONFUSED_MOVE); - if (owner->GetVictim()) - owner->SetTarget(owner->EnsureVictim()->GetGUID()); + + if constexpr (std::is_base_of_v<Creature, T>) + { + if (Unit* victim = owner->GetVictim()) + owner->SetTarget(victim->GetGUID()); + } + else if constexpr (std::is_base_of_v<Player, T>) + owner->StopMoving(); } } -template ConfusedMovementGenerator<Player>::ConfusedMovementGenerator(); -template ConfusedMovementGenerator<Creature>::ConfusedMovementGenerator(); -template MovementGeneratorType ConfusedMovementGenerator<Player>::GetMovementGeneratorType() const; -template MovementGeneratorType ConfusedMovementGenerator<Creature>::GetMovementGeneratorType() const; -template void ConfusedMovementGenerator<Player>::DoInitialize(Player*); -template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*); -template void ConfusedMovementGenerator<Player>::DoReset(Player*); -template void ConfusedMovementGenerator<Creature>::DoReset(Creature*); -template bool ConfusedMovementGenerator<Player>::DoUpdate(Player*, uint32); -template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature*, uint32); -template void ConfusedMovementGenerator<Player>::DoDeactivate(Player*); -template void ConfusedMovementGenerator<Creature>::DoDeactivate(Creature*); +template class ConfusedMovementGenerator<Player>; +template class ConfusedMovementGenerator<Creature>; diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index ff705836a88..569cc519157 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -22,11 +22,14 @@ #include "MoveSplineInit.h" #include "MovementDefines.h" #include "PathGenerator.h" +#include "Player.h" #include "Random.h" template<class T> -RandomMovementGenerator<T>::RandomMovementGenerator(float distance, Optional<Milliseconds> duration, - Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult /*= {}*/) : _timer(0), _reference(), _wanderDistance(distance), _wanderSteps(0) +RandomMovementGenerator<T>::RandomMovementGenerator(float distance, Optional<Milliseconds> duration, Optional<float> speed, + MovementWalkRunSpeedSelectionMode speedSelectionMode, + Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult /*= {}*/) + : _timer(0), _speed(speed), _speedSelectionMode(speedSelectionMode), _wanderDistance(distance), _wanderSteps(0) { this->Mode = MOTION_MODE_DEFAULT; this->Priority = MOTION_PRIORITY_NORMAL; @@ -37,10 +40,6 @@ RandomMovementGenerator<T>::RandomMovementGenerator(float distance, Optional<Mil _duration.emplace(*duration); } -template -RandomMovementGenerator<Creature>::RandomMovementGenerator(float distance, Optional<Milliseconds> duration, - Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult); - template<class T> MovementGeneratorType RandomMovementGenerator<T>::GetMovementGeneratorType() const { @@ -72,26 +71,18 @@ void RandomMovementGenerator<T>::Resume(uint32 overrideTimer) this->RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED); } -template MovementGeneratorType RandomMovementGenerator<Creature>::GetMovementGeneratorType() const; - template<class T> -void RandomMovementGenerator<T>::DoInitialize(T*) { } - -template<> -void RandomMovementGenerator<Creature>::DoInitialize(Creature* owner) +void RandomMovementGenerator<T>::DoInitialize(T* owner) { - RemoveFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); - AddFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED); - if (!owner || !owner->IsAlive()) + if (!owner->IsAlive()) return; _reference = owner->GetPosition(); owner->StopMoving(); - if (_wanderDistance == 0.f) - _wanderDistance = owner->GetWanderDistance(); - // Retail seems to let a creature walk 2 up to 10 splines before triggering a pause _wanderSteps = urand(2, 10); @@ -100,28 +91,19 @@ void RandomMovementGenerator<Creature>::DoInitialize(Creature* owner) } template<class T> -void RandomMovementGenerator<T>::DoReset(T*) { } - -template<> -void RandomMovementGenerator<Creature>::DoReset(Creature* owner) +void RandomMovementGenerator<T>::DoReset(T* owner) { - RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); DoInitialize(owner); } template<class T> -void RandomMovementGenerator<T>::SetRandomLocation(T*) { } - -template<> -void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner) +void RandomMovementGenerator<T>::SetRandomLocation(T* owner) { - if (!owner) - return; - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE | UNIT_STATE_LOST_CONTROL) || owner->IsMovementPreventedByCasting()) { - AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); owner->StopMoving(); _path = nullptr; return; @@ -163,26 +145,30 @@ void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner) return; } - RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); - bool walk = true; - switch (owner->GetMovementTemplate().GetRandom()) + Movement::MoveSplineInit init(owner); + init.MovebyPath(_path->GetPath()); + + switch (_speedSelectionMode) { - case CreatureRandomMovementType::CanRun: - walk = owner->IsWalking(); + case MovementWalkRunSpeedSelectionMode::Default: break; - case CreatureRandomMovementType::AlwaysRun: - walk = false; + case MovementWalkRunSpeedSelectionMode::ForceRun: + init.SetWalk(false); + break; + case MovementWalkRunSpeedSelectionMode::ForceWalk: + init.SetWalk(true); break; default: break; } - Movement::MoveSplineInit init(owner); - init.MovebyPath(_path->GetPath()); - init.SetWalk(walk); + if (_speed) + init.SetVelocity(*_speed); + int32 splineDuration = init.Launch(); --_wanderSteps; @@ -196,22 +182,17 @@ void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner) } // Call for creature group update - owner->SignalFormationMovement(); + if constexpr (std::is_base_of_v<Creature, T>) + owner->SignalFormationMovement(); } template<class T> -bool RandomMovementGenerator<T>::DoUpdate(T*, uint32) +bool RandomMovementGenerator<T>::DoUpdate(T* owner, uint32 diff) { - return false; -} - -template<> -bool RandomMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) -{ - if (!owner || !owner->IsAlive()) + if (!owner->IsAlive()) return true; - if (HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED | MOVEMENTGENERATOR_FLAG_PAUSED)) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED | MOVEMENTGENERATOR_FLAG_PAUSED)) return true; if (_duration) @@ -219,46 +200,40 @@ bool RandomMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) _duration->Update(diff); if (_duration->Passed()) { - RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY); - AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); return false; } } if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) { - AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); owner->StopMoving(); _path = nullptr; return true; } else - RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); _timer.Update(diff); - if ((HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING) && !owner->movespline->Finalized()) || (_timer.Passed() && owner->movespline->Finalized())) + if ((this->HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING) && !owner->movespline->Finalized()) || (_timer.Passed() && owner->movespline->Finalized())) SetRandomLocation(owner); return true; } template<class T> -void RandomMovementGenerator<T>::DoDeactivate(T*) { } - -template<> -void RandomMovementGenerator<Creature>::DoDeactivate(Creature* owner) +void RandomMovementGenerator<T>::DoDeactivate(T* owner) { - AddFlag(MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_DEACTIVATED); owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE); } template<class T> -void RandomMovementGenerator<T>::DoFinalize(T*, bool, bool) { } - -template<> -void RandomMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, bool movementInform) +void RandomMovementGenerator<T>::DoFinalize(T* owner, bool active, bool movementInform) { - AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); if (active) { owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE); @@ -268,15 +243,35 @@ void RandomMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, owner->SetWalk(false); } - if (movementInform && HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) + if (movementInform && this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) { - SetScriptResult(MovementStopReason::Finished); - if (owner->IsAIEnabled()) - owner->AI()->MovementInform(RANDOM_MOTION_TYPE, 0); + this->SetScriptResult(MovementStopReason::Finished); + if constexpr (std::is_base_of_v<Creature, T>) + if (owner->IsAIEnabled()) + owner->AI()->MovementInform(RANDOM_MOTION_TYPE, 0); } } -MovementGenerator* RandomMovementFactory::Create(Unit* /*object*/) const +MovementGenerator* RandomMovementFactory::Create(Unit* object) const { - return new RandomMovementGenerator<Creature>(); + Creature* owner = object->ToCreature(); + MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode::Default; + switch (owner->GetMovementTemplate().GetRandom()) + { + case CreatureRandomMovementType::Walk: + speedSelectionMode = MovementWalkRunSpeedSelectionMode::ForceWalk; + break; + case CreatureRandomMovementType::CanRun: + break; + case CreatureRandomMovementType::AlwaysRun: + speedSelectionMode = MovementWalkRunSpeedSelectionMode::ForceRun; + break; + default: + break; + } + + return new RandomMovementGenerator<Creature>(object->ToCreature()->GetWanderDistance(), {}, {}, speedSelectionMode); } + +template class RandomMovementGenerator<Creature>; +template class RandomMovementGenerator<Player>; diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h index f67b4096e70..51dcd19f8e8 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h @@ -29,7 +29,8 @@ template<class T> class RandomMovementGenerator : public MovementGeneratorMedium<T, RandomMovementGenerator<T>> { public: - explicit RandomMovementGenerator(float distance = 0.0f, Optional<Milliseconds> duration = {}, + explicit RandomMovementGenerator(float distance, Optional<Milliseconds> duration = {}, Optional<float> speed = {}, + MovementWalkRunSpeedSelectionMode speedSelectionMode = MovementWalkRunSpeedSelectionMode::Default, Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult = {}); MovementGeneratorType GetMovementGeneratorType() const override; @@ -51,6 +52,8 @@ class RandomMovementGenerator : public MovementGeneratorMedium<T, RandomMovement std::unique_ptr<PathGenerator> _path; TimeTracker _timer; Optional<TimeTracker> _duration; + Optional<float> _speed; + MovementWalkRunSpeedSelectionMode _speedSelectionMode; Position _reference; float _wanderDistance; uint8 _wanderSteps; diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index f28f37134a4..4bb6db88060 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -554,7 +554,7 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun && (lastWaypointForSegment->Delay || (_isReturningToStart ? _currentNode == 0 : _currentNode == path->Nodes.size() - 1))) init.SetFacing(*lastWaypointForSegment->Orientation); - switch (path->MoveType) + switch (lastWaypointForSegment->MoveType.value_or(path->MoveType)) { case WaypointMoveType::Land: init.SetAnimation(AnimTier::Ground); diff --git a/src/server/game/Movement/Waypoints/WaypointDefines.h b/src/server/game/Movement/Waypoints/WaypointDefines.h index eb388481f71..bd4e3019b29 100644 --- a/src/server/game/Movement/Waypoints/WaypointDefines.h +++ b/src/server/game/Movement/Waypoints/WaypointDefines.h @@ -49,17 +49,17 @@ DEFINE_ENUM_FLAG(WaypointPathFlags); struct WaypointNode { - constexpr WaypointNode() : Id(0), X(0.f), Y(0.f), Z(0.f), MoveType(WaypointMoveType::Walk) { } - constexpr WaypointNode(uint32 id, float x, float y, float z, Optional<float> orientation = { }, Optional<Milliseconds> delay = {}) - : Id(id), X(x), Y(y), Z(z), Orientation(orientation), Delay(delay), MoveType(WaypointMoveType::Walk) { } + constexpr WaypointNode() = default; + constexpr WaypointNode(uint32 id, float x, float y, float z, Optional<float> orientation = {}, Optional<Milliseconds> delay = {}, Optional<WaypointMoveType> moveType = {}) + : Id(id), X(x), Y(y), Z(z), Orientation(orientation), Delay(delay), MoveType(moveType) { } - uint32 Id; - float X; - float Y; - float Z; - Optional<float> Orientation; - Optional<Milliseconds> Delay; - WaypointMoveType MoveType; + uint32 Id = 0; + float X = 0.0f; + float Y = 0.0f; + float Z = 0.0f; + Optional<float> Orientation = {}; + Optional<Milliseconds> Delay = {}; + Optional<WaypointMoveType> MoveType = {}; }; struct WaypointPath diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index 481fdc1a43d..6f51c467da3 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -355,8 +355,8 @@ void WaypointPath::BuildSegments() { ++ContinuousSegments.back().second; - // split on delay - if (i + 1 != Nodes.size() && Nodes[i].Delay) + // split on delay or different move type + if (i + 1 != Nodes.size() && (Nodes[i].Delay || Nodes[i].MoveType != Nodes[i + 1].MoveType)) ContinuousSegments.emplace_back(i, 1); } } diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index b798da637ad..f4592260e21 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -377,6 +377,7 @@ enum QuestObjectiveType QUEST_OBJECTIVE_AREA_TRIGGER_ENTER = 19, QUEST_OBJECTIVE_AREA_TRIGGER_EXIT = 20, QUEST_OBJECTIVE_KILL_WITH_LABEL = 21, + QUEST_OBJECTIVE_UNK_1127 = 22, MAX_QUEST_OBJECTIVE_TYPE }; diff --git a/src/server/game/Server/Packets/PacketOperators.h b/src/server/game/Server/Packets/PacketOperators.h index 7c9fe489dca..1114e83b8f8 100644 --- a/src/server/game/Server/Packets/PacketOperators.h +++ b/src/server/game/Server/Packets/PacketOperators.h @@ -45,9 +45,9 @@ namespace WorldPackets { T const& Value; - friend inline ByteBuffer& operator<<(ByteBuffer& data, AsWriter const& opt) + friend inline ByteBuffer& operator<<(ByteBuffer& data, AsWriter const& as) { - data << static_cast<Underlying>(opt.Value); + data << static_cast<Underlying>(as.Value); return data; } }; @@ -55,11 +55,11 @@ namespace WorldPackets template<AsWritable Underlying, AsWritableFor<Underlying> T> struct AsReaderWriter : AsWriter<Underlying, T> { - friend inline ByteBuffer& operator>>(ByteBuffer& data, AsReaderWriter const& opt) + friend inline ByteBuffer& operator>>(ByteBuffer& data, AsReaderWriter const& as) { Underlying temp; data >> temp; - const_cast<T&>(opt.Value) = static_cast<T>(temp); + const_cast<T&>(as.Value) = static_cast<T>(temp); return data; } }; @@ -199,7 +199,17 @@ namespace WorldPackets { Underlying temp; data >> temp; - const_cast<Container&>(size.Value).resize(temp); + + if constexpr (std::is_same_v<Container, std::string> || std::is_same_v<Container, std::string_view>) + if (size_t rpos = data.rpos(); temp > data.size() - rpos) + data.OnInvalidPosition(rpos, temp); + + if constexpr (std::is_same_v<std::remove_cvref_t<Container>, std::string_view>) + // create a temporary string_view pointing at random position in ByteBuffer to be able to retrieve the length later + const_cast<std::string_view&>(size.Value) = { reinterpret_cast<char const*>(data.data()), temp }; + else + const_cast<Container&>(size.Value).resize(temp); + return data; } }; @@ -215,9 +225,9 @@ namespace WorldPackets { Container const& Value; - friend inline ByteBuffer& operator<<(ByteBuffer& data, BitsSizeWriter const& bits) + friend inline ByteBuffer& operator<<(ByteBuffer& data, BitsSizeWriter const& size) { - data.WriteBits(static_cast<uint32>(bits.Value.size()), BitCount); + data.WriteBits(static_cast<uint32>(size.Value.size()), BitCount); return data; } }; @@ -225,9 +235,9 @@ namespace WorldPackets template<uint32 BitCount, ContainerReadable<uint32> Container> struct BitsSizeReaderWriter : BitsSizeWriter<BitCount, Container> { - friend inline ByteBuffer& operator>>(ByteBuffer& data, BitsSizeReaderWriter const& bits) + friend inline ByteBuffer& operator>>(ByteBuffer& data, BitsSizeReaderWriter const& size) { - const_cast<Container&>(bits.Value).resize(data.ReadBits(BitCount)); + const_cast<Container&>(size.Value).resize(data.ReadBits(BitCount)); return data; } }; @@ -245,8 +255,7 @@ namespace WorldPackets template<typename T> concept StringReadable = StringWritable<T> && !std::is_const_v<T> - && !std::same_as<T, std::string_view> - && requires(T& container) { container.resize(uint32()); } + && (requires(T& container) { container.resize(uint32()); } || std::same_as<T, std::string_view>) && requires(ByteBuffer& data, T& string) { string = data.ReadString(uint32(), bool()); }; namespace SizedString @@ -256,9 +265,9 @@ namespace WorldPackets { Container const& Value; - friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& bits) + friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& size) { - data.WriteBits(static_cast<uint32>(bits.Value.length()), BitCount); + data.WriteBits(static_cast<uint32>(size.Value.length()), BitCount); return data; } }; @@ -266,9 +275,18 @@ namespace WorldPackets template<uint32 BitCount, StringReadable Container> struct SizeReaderWriter : SizeWriter<BitCount, Container> { - friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& bits) + friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& size) { - const_cast<Container&>(bits.Value).resize(data.ReadBits(BitCount)); + uint32 length = data.ReadBits(BitCount); + if (size_t rpos = data.rpos(); length > data.size() - rpos) + data.OnInvalidPosition(rpos, length); + + if constexpr (std::is_same_v<Container, std::string_view>) + // create a temporary string_view pointing at start of ByteBuffer to be able to retrieve the length later + const_cast<std::string_view&>(size.Value) = { reinterpret_cast<char const*>(data.data()), length }; + else + const_cast<Container&>(size.Value).resize(length); + return data; } }; @@ -318,9 +336,9 @@ namespace WorldPackets { Container const& Value; - friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& bits) + friend inline ByteBuffer& operator<<(ByteBuffer& data, SizeWriter const& size) { - data.WriteBits(static_cast<uint32>(bits.Value.length() + 1), BitCount); + data.WriteBits(static_cast<uint32>(size.Value.length() + 1), BitCount); return data; } }; @@ -328,10 +346,20 @@ namespace WorldPackets template<uint32 BitCount, StringReadable Container> struct SizeReaderWriter : SizeWriter<BitCount, Container> { - friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& bits) + friend inline ByteBuffer& operator>>(ByteBuffer& data, SizeReaderWriter const& size) { if (uint32 bytesIncludingNullTerminator = data.ReadBits(BitCount); bytesIncludingNullTerminator > 1) - const_cast<Container&>(bits.Value).resize(bytesIncludingNullTerminator - 1); + { + uint32 length = bytesIncludingNullTerminator - 1; + if (size_t rpos = data.rpos(); length > data.size() - rpos) + data.OnInvalidPosition(rpos, bytesIncludingNullTerminator); + + if constexpr (std::is_same_v<Container, std::string_view>) + // create a temporary string_view pointing at start of ByteBuffer to be able to retrieve the length later + const_cast<std::string_view&>(size.Value) = { reinterpret_cast<char const*>(data.data()), length }; + else + const_cast<Container&>(size.Value).resize(length); + } return data; } }; diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index d2b2b96a69b..a336b5790ad 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -15,11 +15,8 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - \ingroup u2w -*/ - #include "WorldSession.h" +#include "Account.h" #include "AccountMgr.h" #include "AuthenticationPackets.h" #include "Bag.h" @@ -107,17 +104,19 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, - std::string os, Minutes timezoneOffset, uint32 build, ClientBuild::VariantId clientBuildVariant, LocaleConstant locale, uint32 recruiter, bool isARecruiter): +WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::string&& battlenetAccountEmail, + std::shared_ptr<WorldSocket>&& sock, AccountTypes sec, uint8 expansion, time_t mute_time, std::string&& os, Minutes timezoneOffset, + uint32 build, ClientBuild::VariantId clientBuildVariant, LocaleConstant locale, uint32 recruiter, bool isARecruiter) : m_muteTime(mute_time), m_timeOutTime(0), AntiDOS(this), m_GUIDLow(UI64LIT(0)), _player(nullptr), + m_Socket({ std::move(sock), nullptr }), _security(sec), _accountId(id), _accountName(std::move(name)), - _battlenetAccountId(battlenetAccountId), + _battlenetAccount(new Battlenet::Account(this, ObjectGuid::Create<HighGuid::BNetAccount>(battlenetAccountId), std::move(battlenetAccountEmail))), m_accountExpansion(expansion), m_expansion(std::min<uint8>(expansion, sWorld->getIntConfig(CONFIG_EXPANSION))), _os(std::move(os)), @@ -151,14 +150,13 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun _battlePetMgr(std::make_unique<BattlePets::BattlePetMgr>(this)), _collectionMgr(std::make_unique<CollectionMgr>(this)) { - if (sock) + if (m_Socket[CONNECTION_TYPE_REALM]) { - m_Address = sock->GetRemoteIpAddress().to_string(); + m_Address = m_Socket[CONNECTION_TYPE_REALM]->GetRemoteIpAddress().to_string(); ResetTimeOutTime(false); LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = {};", GetAccountId()); // One-time query } - m_Socket[CONNECTION_TYPE_REALM] = std::move(sock); _instanceConnectKey.Raw = UI64LIT(0); } @@ -195,6 +193,16 @@ bool WorldSession::PlayerDisconnected() const m_Socket[CONNECTION_TYPE_INSTANCE] && m_Socket[CONNECTION_TYPE_INSTANCE]->IsOpen()); } +uint32 WorldSession::GetBattlenetAccountId() const +{ + return GetBattlenetAccountGUID().GetCounter(); +} + +ObjectGuid WorldSession::GetBattlenetAccountGUID() const +{ + return _battlenetAccount->GetGUID(); +} + std::string const & WorldSession::GetPlayerName() const { return _player != nullptr ? _player->GetName() : DefaultPlayerName; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 2c55b457ce7..9c5dee6a355 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -15,10 +15,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/// \addtogroup u2w -/// @{ -/// \file - #ifndef __WORLDSESSION_H #define __WORLDSESSION_H @@ -70,6 +66,11 @@ enum InventoryResult : uint8; enum class StableResult : uint8; enum class TabardVendorType : int32; +namespace Battlenet +{ +class Account; +} + namespace BattlePets { class BattlePetMgr; @@ -969,8 +970,9 @@ struct PacketCounter class TC_GAME_API WorldSession { public: - WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time, - std::string os, Minutes timezoneOffset, uint32 build, ClientBuild::VariantId clientBuildVariant, LocaleConstant locale, uint32 recruiter, bool isARecruiter); + WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::string&& battlenetAccountEmail, + std::shared_ptr<WorldSocket>&& sock, AccountTypes sec, uint8 expansion, time_t mute_time, std::string&& os, Minutes timezoneOffset, + uint32 build, ClientBuild::VariantId clientBuildVariant, LocaleConstant locale, uint32 recruiter, bool isARecruiter); ~WorldSession(); bool PlayerLoading() const { return !m_playerLoading.IsEmpty(); } @@ -1006,8 +1008,9 @@ class TC_GAME_API WorldSession uint32 GetAccountId() const { return _accountId; } ObjectGuid GetAccountGUID() const { return ObjectGuid::Create<HighGuid::WowAccount>(GetAccountId()); } std::string const& GetAccountName() const { return _accountName; } - uint32 GetBattlenetAccountId() const { return _battlenetAccountId; } - ObjectGuid GetBattlenetAccountGUID() const { return ObjectGuid::Create<HighGuid::BNetAccount>(GetBattlenetAccountId()); } + uint32 GetBattlenetAccountId() const; + ObjectGuid GetBattlenetAccountGUID() const; + Battlenet::Account& GetBattlenetAccount() const { return *_battlenetAccount; } Player* GetPlayer() const { return _player; } std::string const& GetPlayerName() const; std::string GetPlayerInfo() const; @@ -1964,14 +1967,14 @@ class TC_GAME_API WorldSession ObjectGuid::LowType m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set) Player* _player; - std::shared_ptr<WorldSocket> m_Socket[MAX_CONNECTION_TYPES]; + std::array<std::shared_ptr<WorldSocket>, MAX_CONNECTION_TYPES> m_Socket; std::string m_Address; // Current Remote Address // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session! AccountTypes _security; uint32 _accountId; std::string _accountName; - uint32 _battlenetAccountId; + std::unique_ptr<Battlenet::Account> _battlenetAccount; uint8 m_accountExpansion; uint8 m_expansion; std::string _os; @@ -2028,4 +2031,3 @@ class TC_GAME_API WorldSession }; #endif -/// @} diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index a701939c313..3aa0064b34c 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -29,6 +29,7 @@ #include "IpBanCheckConnectionInitializer.h" #include "PacketLog.h" #include "ProtobufJSON.h" +#include "QueryResultStructured.h" #include "RealmList.h" #include "RBAC.h" #include "RealmList.pb.h" @@ -616,6 +617,7 @@ struct AccountInfo struct { uint32 Id; + std::string Email; bool IsLockedToIP; std::string LastIP; std::string LockCountry; @@ -633,39 +635,42 @@ struct AccountInfo uint32 Recruiter; std::string OS; Minutes TimezoneOffset; - bool IsRectuiter; + bool IsRecruiter; AccountTypes Security; bool IsBanned; } Game; bool IsBanned() const { return BattleNet.IsBanned || Game.IsBanned; } - explicit AccountInfo(Field const* fields) + explicit AccountInfo(PreparedResultSet const* result) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 - // SELECT a.id, a.session_key, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, a.client_build, a.locale, a.recruiter, a.os, a.timezone_offset, ba.id, aa.SecurityLevel, - // 14 15 16 - // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id - // FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id LEFT JOIN account_access aa ON a.id = aa.AccountID AND aa.RealmID IN (-1, ?) - // LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id LEFT JOIN account r ON a.id = r.recruiter - // WHERE a.username = ? AND LENGTH(a.session_key) = 40 ORDER BY aa.RealmID DESC LIMIT 1 - Game.Id = fields[0].GetUInt32(); - Game.KeyData = fields[1].GetBinary<64>(); - BattleNet.LastIP = fields[2].GetString(); - BattleNet.IsLockedToIP = fields[3].GetBool(); - BattleNet.LockCountry = fields[4].GetString(); - Game.Expansion = fields[5].GetUInt8(); - Game.MuteTime = fields[6].GetInt64(); - Game.Build = fields[7].GetUInt32(); - Game.Locale = LocaleConstant(fields[8].GetUInt8()); - Game.Recruiter = fields[9].GetUInt32(); - Game.OS = fields[10].GetString(); - Game.TimezoneOffset = Minutes(fields[11].GetInt16()); - BattleNet.Id = fields[12].GetUInt32(); - Game.Security = AccountTypes(fields[13].GetUInt8()); - BattleNet.IsBanned = fields[14].GetUInt32() != 0; - Game.IsBanned = fields[15].GetUInt32() != 0; - Game.IsRectuiter = fields[16].GetUInt32() != 0; + // SELECT a.id AS accountId, a.session_key_bnet, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, a.client_build, a.locale, a.recruiter, a.os, a.timezone_offset, ba.id AS bnet_account_id, ba.email as bnet_account_email, aa.SecurityLevel, + // bab.unbandate > UNIX_TIMESTAMP() OR bab.unbandate = bab.bandate AS is_bnet_banned, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate AS is_banned, r.id AS recruitId + // FROM account a LEFT JOIN account r ON a.id = r.recruiter LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id + // LEFT JOIN account_access aa ON a.id = aa.AccountID AND aa.RealmID IN (-1, ?) LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 + // WHERE a.username = ? AND LENGTH(a.session_key_bnet) = 64 ORDER BY aa.RealmID DESC LIMIT 1 + + DEFINE_FIELD_ACCESSOR_CACHE_ANONYMOUS(PreparedResultSet, (account_id)(session_key_bnet)(last_ip)(locked)(lock_country)(expansion)(mutetime)(client_build) + (locale)(recruiter)(os)(timezone_offset)(bnet_account_id)(bnet_account_email)(SecurityLevel)(is_bnet_banned)(is_banned)(recruitId)) fields { *result }; + + Game.Id = fields.account_id().GetUInt32(); + Game.KeyData = fields.session_key_bnet().GetBinary<64>(); + BattleNet.LastIP = fields.last_ip().GetStringView(); + BattleNet.IsLockedToIP = fields.locked().GetBool(); + BattleNet.LockCountry = fields.lock_country().GetStringView(); + Game.Expansion = fields.expansion().GetUInt8(); + Game.MuteTime = fields.mutetime().GetInt64(); + Game.Build = fields.client_build().GetUInt32(); + Game.Locale = LocaleConstant(fields.locale().GetUInt8()); + Game.Recruiter = fields.recruiter().GetUInt32(); + Game.OS = fields.os().GetStringView(); + Game.TimezoneOffset = Minutes(fields.timezone_offset().GetInt16()); + BattleNet.Id = fields.bnet_account_id().GetUInt32(); + BattleNet.Email = fields.bnet_account_email().GetStringView(); + Game.Security = AccountTypes(fields.SecurityLevel().GetUInt8()); + BattleNet.IsBanned = fields.is_bnet_banned().GetUInt32() != 0; + Game.IsBanned = fields.is_banned().GetUInt32() != 0; + Game.IsRecruiter = fields.recruitId().GetUInt32() != 0; if (Game.Locale >= TOTAL_LOCALES) Game.Locale = LOCALE_enUS; @@ -707,7 +712,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: std::string address = GetRemoteIpAddress().to_string(); - AccountInfo account(result->Fetch()); + AccountInfo account(result.get()); ClientBuild::Info const* buildInfo = ClientBuild::GetBuildInfo(account.Game.Build); if (!buildInfo) @@ -887,9 +892,9 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<WorldPackets::Auth:: _authed = true; _worldSession = new WorldSession(account.Game.Id, std::move(*joinTicket->mutable_gameaccount()), account.BattleNet.Id, - static_pointer_cast<WorldSocket>(shared_from_this()), account.Game.Security, account.Game.Expansion, mutetime, - account.Game.OS, account.Game.TimezoneOffset, account.Game.Build, buildVariant, account.Game.Locale, - account.Game.Recruiter, account.Game.IsRectuiter); + std::move(account.BattleNet.Email), static_pointer_cast<WorldSocket>(shared_from_this()), account.Game.Security, + account.Game.Expansion, mutetime, std::move(account.Game.OS), account.Game.TimezoneOffset, account.Game.Build, buildVariant, + account.Game.Locale, account.Game.Recruiter, account.Game.IsRecruiter); QueueQuery(_worldSession->LoadPermissionsAsync().WithPreparedCallback([this](PreparedQueryResult result) { diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 49aa00c14e0..73fcbe1cefe 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5629,7 +5629,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const // Consecrate ticks can miss and will not show up in the combat log // dynobj auras must always have a caster if (GetSpellEffectInfo().IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) && - ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE) + ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false, true) != SPELL_MISS_NONE) return; CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL); @@ -5763,7 +5763,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c // dynobj auras must always have a caster if (GetSpellEffectInfo().IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) && - ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE) + ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false, true) != SPELL_MISS_NONE) return; CleanDamage cleanDamage = CleanDamage(0, 0, GetSpellInfo()->GetAttackType(), MELEE_HIT_NORMAL); @@ -5856,7 +5856,7 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster) if (!caster || !caster->IsAlive() || !target->IsAlive()) return; - if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo(), &GetSpellEffectInfo())) + if (target->IsImmunedToAuraPeriodicTick(caster, this)) { SendTickImmune(target, caster); return; @@ -5886,7 +5886,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const if (!target->IsAlive()) return; - if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo(), &GetSpellEffectInfo())) + if (target->IsImmunedToAuraPeriodicTick(caster, this)) { SendTickImmune(target, caster); return; @@ -5946,14 +5946,14 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con if (!caster || !caster->IsAlive() || !target->IsAlive() || target->GetPowerType() != powerType) return; - if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo(), &GetSpellEffectInfo())) + if (target->IsImmunedToAuraPeriodicTick(caster, this)) { SendTickImmune(target, caster); return; } if (GetSpellEffectInfo().IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) && - caster->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE) + caster->SpellHitResult(target, GetSpellInfo(), false, true) != SPELL_MISS_NONE) return; // ignore negative values (can be result apply spellmods to aura damage @@ -6009,7 +6009,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const if (!target->IsAlive() || !target->GetMaxPower(powerType)) return; - if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo(), &GetSpellEffectInfo())) + if (target->IsImmunedToAuraPeriodicTick(caster, this)) { SendTickImmune(target, caster); return; @@ -6047,7 +6047,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons if (!target->IsAlive() || !target->GetMaxPower(powerType)) return; - if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo(), &GetSpellEffectInfo())) + if (target->IsImmunedToAuraPeriodicTick(caster, this)) { SendTickImmune(target, caster); return; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 7a4722f9cef..3a68549f238 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -684,7 +684,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) { // needs readding - remove now, will be applied in next update cycle // (dbcs do not have auras which apply on same type of targets but have different radius, so this is not really needed) - if (itr->first->IsImmunedToSpell(GetSpellInfo(), caster, true) || !CanBeAppliedOn(itr->first)) + if (itr->first->IsImmunedToSpell(GetSpellInfo(), itr->second, caster, true) || !CanBeAppliedOn(itr->first)) { targetsToRemove.push_back(applicationPair.second->GetTarget()); continue; @@ -717,7 +717,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), spellEffectInfo, caster)) itr->second &= ~(1 << spellEffectInfo.EffectIndex); - if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), caster) || !CanBeAppliedOn(itr->first)) + if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), itr->second, caster) || !CanBeAppliedOn(itr->first)) addUnit = false; } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 50db01b3fec..86e1d7f292a 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2434,7 +2434,9 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // Calculate hit result WorldObject* caster = m_originalCaster ? m_originalCaster : m_caster; - targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target))); + targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo, + m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target)), + false /*immunity will be checked after complete EffectMask is known*/); // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. @@ -2479,7 +2481,9 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= { // Calculate reflected spell result on caster (shouldn't be able to reflect gameobject spells) Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit()); - targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo, false); // can't reflect twice + targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo, + false /*can't reflect twice*/, + false /*immunity will be checked after complete EffectMask is known*/); // Proc spell reflect aura when missile hits the original target target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(Milliseconds(targetInfo.TimeDelay))); @@ -3099,7 +3103,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo) return SPELL_MISS_EVADE; // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case - if (hitInfo.TimeDelay && unit->IsImmunedToSpell(m_spellInfo, m_caster)) + if (hitInfo.TimeDelay && unit->IsImmunedToSpell(m_spellInfo, hitInfo.EffectMask, m_caster)) return SPELL_MISS_IMMUNE; CallScriptBeforeHitHandlers(hitInfo.MissCondition); @@ -3620,8 +3624,6 @@ void Spell::cancel() SendChannelUpdate(0, SPELL_FAILED_INTERRUPTED); SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); - - m_appliedMods.clear(); break; default: break; @@ -5086,7 +5088,7 @@ static std::pair<int32, SpellHealPredictionType> CalcPredictedHealing(SpellInfo case SPELL_AURA_OBS_MOD_HEALTH: points += unitCaster->SpellHealingBonusDone(target, spellInfo, spellEffectInfo.CalcValue(unitCaster, nullptr, target, nullptr, castItemEntry, castItemLevel), - DIRECT_DAMAGE, spellEffectInfo, 1, spell) * spellInfo->GetMaxTicks(); + DIRECT_DAMAGE, spellEffectInfo, 1, spell) * spellEffectInfo.GetPeriodicTickCount(); break; case SPELL_AURA_PERIODIC_TRIGGER_SPELL: if (SpellInfo const* triggered = sSpellMgr->GetSpellInfo(spellEffectInfo.TriggerSpell, spellInfo->Difficulty)) @@ -8612,6 +8614,10 @@ void Spell::PreprocessSpellLaunch(TargetInfo& targetInfo) if (!targetUnit) return; + // Check immunity now that EffectMask is known + if (targetUnit->IsImmunedToSpell(GetSpellInfo(), targetInfo.EffectMask, m_caster)) + targetInfo.MissCondition = SPELL_MISS_IMMUNE; + // This will only cause combat - the target will engage once the projectile hits (in Spell::TargetInfo::PreprocessTarget) if (m_originalCaster && targetInfo.MissCondition != SPELL_MISS_EVADE && !m_originalCaster->IsFriendlyTo(targetUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || targetUnit->IsEngaged())) m_originalCaster->SetInCombatWith(targetUnit, true); @@ -8622,7 +8628,11 @@ void Spell::PreprocessSpellLaunch(TargetInfo& targetInfo) unit = targetUnit; // In case spell reflect from target, do all effect on caster (if hit) else if (targetInfo.MissCondition == SPELL_MISS_REFLECT && targetInfo.ReflectResult == SPELL_MISS_NONE) + { unit = m_caster->ToUnit(); + if (unit && unit->IsImmunedToSpell(GetSpellInfo(), targetInfo.EffectMask, unit)) + targetInfo.ReflectResult = SPELL_MISS_IMMUNE; + } if (!unit) return; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 700af4e4b90..aa38b862f18 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -494,6 +494,23 @@ bool SpellEffectInfo::IsUnitOwnedAuraEffect() const return IsAreaAuraEffect() || Effect == SPELL_EFFECT_APPLY_AURA || Effect == SPELL_EFFECT_APPLY_AURA_ON_PET; } +uint32 SpellEffectInfo::GetPeriodicTickCount() const +{ + if (!ApplyAuraPeriod) + return 0; + + int32 duration = _spellInfo->GetDuration(); + // skip infinite periodics + if (duration <= 0) + return 0; + + uint32 totalTicks = static_cast<uint32>(duration) / ApplyAuraPeriod; + if (_spellInfo->HasAttribute(SPELL_ATTR5_EXTRA_INITIAL_PERIOD)) + ++totalTicks; + + return totalTicks; +} + int32 SpellEffectInfo::CalcValue(WorldObject const* caster /*= nullptr*/, int32 const* bp /*= nullptr*/, Unit const* target /*= nullptr*/, float* variance /*= nullptr*/, uint32 castItemId /*= 0*/, int32 itemLevel /*= -1*/) const { double basePointsPerLevel = RealPointsPerLevel; @@ -3601,8 +3618,8 @@ void SpellInfo::_LoadSqrtTargetLimit(int32 maxTargets, int32 numNonDiminishedTar maxTargetValueHolder = sSpellMgr->GetSpellInfo(*maxTargetsValueHolderSpell, Difficulty); if (!maxTargetValueHolder) - TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} does not exist", maxTargetsValueHolderSpell); - else if (maxTargetsValueHolderEffect >= maxTargetValueHolder->GetEffects().size()) + TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} does not exist", *maxTargetsValueHolderSpell); + else if (*maxTargetsValueHolderEffect >= maxTargetValueHolder->GetEffects().size()) TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {} does not have effect {}", maxTargetValueHolder->Id, AsUnderlyingType(*maxTargetsValueHolderEffect)); else @@ -3622,10 +3639,10 @@ void SpellInfo::_LoadSqrtTargetLimit(int32 maxTargets, int32 numNonDiminishedTar numNonDiminishedTargetsValueHolder = sSpellMgr->GetSpellInfo(*numNonDiminishedTargetsValueHolderSpell, Difficulty); if (!numNonDiminishedTargetsValueHolder) - TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} does not exist", maxTargetsValueHolderSpell); - else if (numNonDiminishedTargetsValueHolderEffect >= numNonDiminishedTargetsValueHolder->GetEffects().size()) + TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} does not exist", *numNonDiminishedTargetsValueHolderSpell); + else if (*numNonDiminishedTargetsValueHolderEffect >= numNonDiminishedTargetsValueHolder->GetEffects().size()) TC_LOG_ERROR("spells", "SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {} does not have effect {}", - numNonDiminishedTargetsValueHolder->Id, AsUnderlyingType(*maxTargetsValueHolderEffect)); + numNonDiminishedTargetsValueHolder->Id, AsUnderlyingType(*numNonDiminishedTargetsValueHolderEffect)); else { SpellEffectInfo const& valueHolder = numNonDiminishedTargetsValueHolder->GetEffect(*numNonDiminishedTargetsValueHolderEffect); @@ -3649,7 +3666,7 @@ void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const& s if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) { - target->RemoveAppliedAuras([this, target, schoolImmunity](AuraApplication const* aurApp) -> bool + target->RemoveAppliedAuras([this, schoolImmunity](AuraApplication const* aurApp) -> bool { SpellInfo const* auraSpellInfo = aurApp->GetBase()->GetSpellInfo(); if (auraSpellInfo->Id == Id) // Don't remove self @@ -3660,12 +3677,8 @@ void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const& s return false; if (!CanDispelAura(auraSpellInfo)) return false; - if (!HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)) - { - WorldObject const* existingAuraCaster = aurApp->GetBase()->GetWorldObjectCaster(); - if (existingAuraCaster && existingAuraCaster->IsFriendlyTo(target)) // Check spell vs aura possitivity - return false; - } + if (aurApp->IsPositive() && !HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)) // Check spell vs aura possitivity + return false; return true; }); } @@ -3944,48 +3957,6 @@ uint32 SpellInfo::CalcCastTime(Spell* spell /*= nullptr*/) const return (castTime > 0) ? uint32(castTime) : 0; } -uint32 SpellInfo::GetMaxTicks() const -{ - uint32 totalTicks = 0; - int32 DotDuration = GetDuration(); - - for (SpellEffectInfo const& effect : GetEffects()) - { - if (effect.IsEffect(SPELL_EFFECT_APPLY_AURA)) - { - switch (effect.ApplyAuraName) - { - case SPELL_AURA_PERIODIC_DAMAGE: - case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: - case SPELL_AURA_PERIODIC_HEAL: - case SPELL_AURA_OBS_MOD_HEALTH: - case SPELL_AURA_OBS_MOD_POWER: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT: - case SPELL_AURA_POWER_BURN: - case SPELL_AURA_PERIODIC_LEECH: - case SPELL_AURA_PERIODIC_MANA_LEECH: - case SPELL_AURA_PERIODIC_ENERGIZE: - case SPELL_AURA_PERIODIC_DUMMY: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL: - case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE: - case SPELL_AURA_PERIODIC_HEALTH_FUNNEL: - // skip infinite periodics - if (effect.ApplyAuraPeriod > 0 && DotDuration > 0) - { - totalTicks = static_cast<uint32>(DotDuration) / effect.ApplyAuraPeriod; - if (HasAttribute(SPELL_ATTR5_EXTRA_INITIAL_PERIOD)) - ++totalTicks; - } - break; - default: - break; - } - } - } - - return totalTicks; -} - uint32 SpellInfo::GetRecoveryTime() const { return RecoveryTime > CategoryRecoveryTime ? RecoveryTime : CategoryRecoveryTime; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index be276ca5738..6897435a461 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -261,6 +261,8 @@ public: bool IsAreaAuraEffect() const; bool IsUnitOwnedAuraEffect() const; + uint32 GetPeriodicTickCount() const; + int32 CalcValue(WorldObject const* caster = nullptr, int32 const* basePoints = nullptr, Unit const* target = nullptr, float* variance = nullptr, uint32 castItemId = 0, int32 itemLevel = -1) const; int32 CalcBaseValue(WorldObject const* caster, Unit const* target, uint32 itemId, int32 itemLevel) const; float CalcValueMultiplier(WorldObject* caster, Spell* spell = nullptr) const; @@ -554,8 +556,6 @@ class TC_GAME_API SpellInfo int32 GetDuration() const; int32 GetMaxDuration() const; - uint32 GetMaxTicks() const; - uint32 CalcCastTime(Spell* spell = nullptr) const; uint32 GetRecoveryTime() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 93e7a0a4c82..41d5c172dcb 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -5470,6 +5470,12 @@ void SpellMgr::LoadSpellInfoTargetCaps() spellInfo->_LoadSqrtTargetLimit(5, 0, 390163, EFFECT_0, {}, {}); }); + // Burning Vehemence + ApplySpellFix({ 400370 }, [](SpellInfo* spellInfo) + { + spellInfo->_LoadSqrtTargetLimit(5, 0, {}, EFFECT_1, {}, {}); + }); + TC_LOG_INFO("server.loading", ">> Loaded SpellInfo target caps in {} ms", GetMSTimeDiffToNow(oldMSTime)); } diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index b08173a1cd4..30cea7a5f15 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -272,7 +272,8 @@ DEFINE_ENUM_FLAG(ProcAttributes); PROC_ATTR_REQ_POWER_COST | \ PROC_ATTR_REQ_SPELLMOD | \ PROC_ATTR_USE_STACKS_FOR_CHARGES | \ - PROC_ATTR_REDUCE_PROC_60) + PROC_ATTR_REDUCE_PROC_60 | \ + PROC_ATTR_CANT_PROC_FROM_ITEM_CAST) struct SpellProcEntry { diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 149c063bf6a..bb5a8acf7c6 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1197,7 +1197,8 @@ void World::LoadConfigSettings(bool reload) _gameRules = { - { .Rule = ::GameRule::TransmogEnabled, .Value = true } + { .Rule = ::GameRule::TransmogEnabled, .Value = true }, + { .Rule = ::GameRule::HousingEnabled, .Value = true } }; if (reload) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index 8cbb907ef3f..08708ff647c 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -958,7 +958,7 @@ struct npc_thorim_trashAI : public ScriptedAI heal += spellEffectInfo.CalcValue(caster); if (spellEffectInfo.IsEffect(SPELL_EFFECT_APPLY_AURA) && spellEffectInfo.IsAura(SPELL_AURA_PERIODIC_HEAL)) - heal += spellInfo->GetMaxTicks() * spellEffectInfo.CalcValue(caster); + heal += spellEffectInfo.GetPeriodicTickCount() * spellEffectInfo.CalcValue(caster); } return heal; } diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 2773f68a067..ee2f8073114 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -223,7 +223,7 @@ class spell_dru_astral_smolder : public AuraScript bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellEffect({ { SPELL_DRUID_ASTRAL_SMOLDER_DAMAGE, EFFECT_0 } }) - && sSpellMgr->AssertSpellInfo(SPELL_DRUID_ASTRAL_SMOLDER_DAMAGE, DIFFICULTY_NONE)->GetMaxTicks(); + && sSpellMgr->AssertSpellInfo(SPELL_DRUID_ASTRAL_SMOLDER_DAMAGE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } bool CheckProc(AuraEffect const* /*aurEff*/, ProcEventInfo const& eventInfo) const @@ -235,10 +235,10 @@ class spell_dru_astral_smolder : public AuraScript { PreventDefaultAction(); - SpellInfo const* astralSmolderDmg = sSpellMgr->AssertSpellInfo(SPELL_DRUID_ASTRAL_SMOLDER_DAMAGE, GetCastDifficulty()); + SpellEffectInfo const& astralSmolderDmg = sSpellMgr->AssertSpellInfo(SPELL_DRUID_ASTRAL_SMOLDER_DAMAGE, GetCastDifficulty())->GetEffect(EFFECT_0); int32 pct = aurEff->GetAmount(); - int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / astralSmolderDmg->GetMaxTicks()); + int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / astralSmolderDmg.GetPeriodicTickCount()); CastSpellExtraArgs args(aurEff); args.AddSpellMod(SPELLVALUE_BASE_POINT0, amount); @@ -2020,7 +2020,8 @@ class spell_dru_t10_balance_4p_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_DRUID_LANGUISH }); + return ValidateSpellEffect({ { SPELL_DRUID_LANGUISH, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_DRUID_LANGUISH, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect* aurEff, ProcEventInfo& eventInfo) @@ -2034,11 +2035,10 @@ class spell_dru_t10_balance_4p_bonus : public AuraScript Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_DRUID_LANGUISH, GetCastDifficulty()); + SpellEffectInfo const& spellEffect = sSpellMgr->AssertSpellInfo(SPELL_DRUID_LANGUISH, GetCastDifficulty())->GetEffect(EFFECT_0); int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); - ASSERT(spellInfo->GetMaxTicks() > 0); - amount /= spellInfo->GetMaxTicks(); + amount /= spellEffect.GetPeriodicTickCount(); CastSpellExtraArgs args(aurEff); args.AddSpellMod(SPELLVALUE_BASE_POINT0, amount); diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 9eb1ee51fd6..46e0ea7d61c 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -690,15 +690,13 @@ class spell_hun_master_marksman : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_HUNTER_MASTER_MARKSMAN }); + return ValidateSpellEffect({ { SPELL_HUNTER_MASTER_MARKSMAN, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_HUNTER_MASTER_MARKSMAN, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } static void HandleProc(AuraScript const&, AuraEffect const* aurEff, ProcEventInfo const& eventInfo) { - uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_MASTER_MARKSMAN, DIFFICULTY_NONE)->GetMaxTicks(); - if (!ticks) - return; - + uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_MASTER_MARKSMAN, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount(); int32 damage = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()) / ticks; eventInfo.GetActor()->CastSpell(eventInfo.GetActionTarget(), SPELL_HUNTER_MASTER_MARKSMAN, CastSpellExtraArgsInit{ @@ -999,8 +997,8 @@ class spell_hun_rejuvenating_wind : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_HUNTER_REJUVENATING_WIND_HEAL }) - && sSpellMgr->AssertSpellInfo(SPELL_HUNTER_REJUVENATING_WIND_HEAL, DIFFICULTY_NONE)->GetMaxTicks() > 0; + return ValidateSpellEffect({ { SPELL_HUNTER_REJUVENATING_WIND_HEAL, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_HUNTER_REJUVENATING_WIND_HEAL, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& /*procEvent*/) @@ -1009,7 +1007,7 @@ class spell_hun_rejuvenating_wind : public AuraScript Unit* caster = GetTarget(); - uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_REJUVENATING_WIND_HEAL, DIFFICULTY_NONE)->GetMaxTicks(); + uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_REJUVENATING_WIND_HEAL, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount(); int32 heal = CalculatePct(caster->GetMaxHealth(), aurEff->GetAmount()) / ticks; caster->CastSpell(caster, SPELL_HUNTER_REJUVENATING_WIND_HEAL, CastSpellExtraArgsInit{ @@ -1399,7 +1397,7 @@ class spell_hun_t29_2p_marksmanship_bonus : public AuraScript bool Validate(SpellInfo const* /*spellInfo*/) override { return ValidateSpellEffect({ { SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE, EFFECT_0 } }) - && sSpellMgr->AssertSpellInfo(SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE, DIFFICULTY_NONE)->GetMaxTicks(); + && sSpellMgr->AssertSpellInfo(SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount(); } void HandleProc(AuraEffect* aurEff, ProcEventInfo& eventInfo) @@ -1407,7 +1405,7 @@ class spell_hun_t29_2p_marksmanship_bonus : public AuraScript PreventDefaultAction(); Unit* caster = eventInfo.GetActor(); - uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE, DIFFICULTY_NONE)->GetMaxTicks(); + uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount(); uint32 damage = CalculatePct(eventInfo.GetDamageInfo()->GetOriginalDamage(), aurEff->GetAmount()) / ticks; caster->CastSpell(eventInfo.GetActionTarget(), SPELL_HUNTER_T29_2P_MARKSMANSHIP_DAMAGE, CastSpellExtraArgs(aurEff) diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index e6462e46257..59155ad13ea 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -1292,7 +1292,9 @@ class spell_mage_ignite : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_MAGE_IGNITE, SPELL_MAGE_HOT_STREAK, SPELL_MAGE_PYROBLAST, SPELL_MAGE_FLAMESTRIKE }); + return ValidateSpellInfo({ SPELL_MAGE_HOT_STREAK, SPELL_MAGE_PYROBLAST, SPELL_MAGE_FLAMESTRIKE }) + && ValidateSpellEffect({ { SPELL_MAGE_IGNITE, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_MAGE_IGNITE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } bool CheckProc(ProcEventInfo& eventInfo) @@ -1304,14 +1306,13 @@ class spell_mage_ignite : public AuraScript { PreventDefaultAction(); - SpellInfo const* igniteDot = sSpellMgr->AssertSpellInfo(SPELL_MAGE_IGNITE, GetCastDifficulty()); + SpellEffectInfo const& igniteDot = sSpellMgr->AssertSpellInfo(SPELL_MAGE_IGNITE, GetCastDifficulty())->GetEffect(EFFECT_0); int32 pct = aurEff->GetAmount(); - ASSERT(igniteDot->GetMaxTicks() > 0); if (spell_mage_hot_streak_ignite_marker::IsActive(eventInfo.GetProcSpell())) pct *= 2; - int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks()); + int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot.GetPeriodicTickCount()); CastSpellExtraArgs args(aurEff); args.AddSpellMod(SPELLVALUE_BASE_POINT0, amount); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 3bbf48b8d47..6ce5d06536a 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -1696,7 +1696,8 @@ class spell_pal_t8_2p_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_PALADIN_HOLY_MENDING }); + return ValidateSpellEffect({ { SPELL_PALADIN_HOLY_MENDING, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_MENDING, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect* aurEff, ProcEventInfo& eventInfo) @@ -1710,11 +1711,10 @@ class spell_pal_t8_2p_bonus : public AuraScript Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_MENDING, GetCastDifficulty()); + SpellEffectInfo const& hotEffect = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_HOLY_MENDING, GetCastDifficulty())->GetEffect(EFFECT_0); int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); - ASSERT(spellInfo->GetMaxTicks() > 0); - amount /= spellInfo->GetMaxTicks(); + amount /= hotEffect.GetPeriodicTickCount(); CastSpellExtraArgs args(aurEff); args.AddSpellBP0(amount); @@ -1732,7 +1732,8 @@ class spell_pal_t30_2p_protection_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_PALADIN_T30_2P_HEARTFIRE_DAMAGE }); + return ValidateSpellEffect({ { SPELL_PALADIN_T30_2P_HEARTFIRE_DAMAGE, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_PALADIN_T30_2P_HEARTFIRE_DAMAGE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect* aurEff, ProcEventInfo& procInfo) @@ -1740,7 +1741,7 @@ class spell_pal_t30_2p_protection_bonus : public AuraScript PreventDefaultAction(); Unit* caster = procInfo.GetActor(); - uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_T30_2P_HEARTFIRE_DAMAGE, DIFFICULTY_NONE)->GetMaxTicks(); + uint32 ticks = sSpellMgr->AssertSpellInfo(SPELL_PALADIN_T30_2P_HEARTFIRE_DAMAGE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount(); uint32 damage = CalculatePct(procInfo.GetDamageInfo()->GetOriginalDamage(), aurEff->GetAmount()) / ticks; caster->CastSpell(procInfo.GetActionTarget(), SPELL_PALADIN_T30_2P_HEARTFIRE_DAMAGE, CastSpellExtraArgs(aurEff) diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 712705d43ad..ff4ce39edbd 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -26,7 +26,6 @@ #include "Containers.h" #include "G3DPosition.hpp" #include "GridNotifiers.h" -#include "ListUtils.h" #include "Log.h" #include "MoveSplineInitArgs.h" #include "ObjectAccessor.h" @@ -55,6 +54,7 @@ enum PriestSpells SPELL_PRIEST_ATONEMENT_EFFECT = 194384, SPELL_PRIEST_ATONEMENT_HEAL = 81751, SPELL_PRIEST_BENEDICTION = 193157, + SPELL_PRIEST_BINDING_HEALS_HEAL = 368276, SPELL_PRIEST_BLAZE_OF_LIGHT = 215768, SPELL_PRIEST_BLAZE_OF_LIGHT_INCREASE = 355851, SPELL_PRIEST_BLAZE_OF_LIGHT_DECREASE = 356084, @@ -62,6 +62,9 @@ enum PriestSpells SPELL_PRIEST_BLESSED_LIGHT = 196813, SPELL_PRIEST_BODY_AND_SOUL = 64129, SPELL_PRIEST_BODY_AND_SOUL_SPEED = 65081, + SPELL_PRIEST_BURNING_VEHEMENCE_DAMAGE = 400370, + SPELL_PRIEST_CASTIGATION = 193134, + SPELL_PRIEST_CENSURE = 200199, SPELL_PRIEST_CIRCLE_OF_HEALING = 204883, SPELL_PRIEST_CRYSTALLINE_REFLECTION = 373457, SPELL_PRIEST_CRYSTALLINE_REFLECTION_HEAL = 373462, @@ -90,7 +93,15 @@ enum PriestSpells SPELL_PRIEST_DIVINE_STAR_SHADOW_DAMAGE = 390845, SPELL_PRIEST_DIVINE_STAR_SHADOW_HEAL = 390981, SPELL_PRIEST_DIVINE_WRATH = 40441, + SPELL_PRIEST_DIVINITY = 1215241, + SPELL_PRIEST_DIVINITY_AURA = 1216314, SPELL_PRIEST_EMPOWERED_RENEW = 391339, + SPELL_PRIEST_EMPOWERED_RENEW_HEAL = 391359, + SPELL_PRIEST_ENTROPIC_RIFT = 447444, + SPELL_PRIEST_ENTROPIC_RIFT_AREATRIGGER = 447445, + SPELL_PRIEST_ENTROPIC_RIFT_AURA = 450193, + SPELL_PRIEST_ENTROPIC_RIFT_DAMAGE = 447448, + SPELL_PRIEST_ENTROPIC_RIFT_PERIODIC = 459314, SPELL_PRIEST_EPIPHANY = 414553, SPELL_PRIEST_EPIPHANY_HIGHLIGHT = 414556, SPELL_PRIEST_ESSENCE_DEVOURER = 415479, @@ -109,6 +120,8 @@ enum PriestSpells SPELL_PRIEST_HALO_HOLY_HEAL = 120692, SPELL_PRIEST_HALO_SHADOW_DAMAGE = 390964, SPELL_PRIEST_HALO_SHADOW_HEAL = 390971, + SPELL_PRIEST_HARSH_DISCIPLINE = 373180, + SPELL_PRIEST_HARSH_DISCIPLINE_AURA = 373183, SPELL_PRIEST_HEAL = 2060, SPELL_PRIEST_HEALING_LIGHT = 196809, SPELL_PRIEST_HEAVENS_WRATH = 421558, @@ -116,6 +129,8 @@ enum PriestSpells SPELL_PRIEST_HOLY_MENDING_HEAL = 391156, SPELL_PRIEST_HOLY_NOVA = 132157, SPELL_PRIEST_HOLY_WORD_CHASTISE = 88625, + SPELL_PRIEST_HOLY_WORD_CHASTISE_INCAPACITATE = 200196, + SPELL_PRIEST_HOLY_WORD_CHASTISE_STUN = 200200, SPELL_PRIEST_HOLY_WORD_SALVATION = 265202, SPELL_PRIEST_HOLY_WORD_SANCTIFY = 34861, SPELL_PRIEST_HOLY_WORD_SERENITY = 2050, @@ -124,6 +139,7 @@ enum PriestSpells SPELL_PRIEST_HOLY_10_1_CLASS_SET_4P_EFFECT = 409479, SPELL_PRIEST_INDEMNITY = 373049, SPELL_PRIEST_ITEM_EFFICIENCY = 37595, + SPELL_PRIEST_LASTING_WORDS = 471504, SPELL_PRIEST_LEAP_OF_FAITH_EFFECT = 92832, SPELL_PRIEST_LEVITATE_EFFECT = 111759, SPELL_PRIEST_LIGHT_ERUPTION = 196812, @@ -131,6 +147,7 @@ enum PriestSpells SPELL_PRIEST_MASOCHISM_TALENT = 193063, SPELL_PRIEST_MASOCHISM_PERIODIC_HEAL = 193065, SPELL_PRIEST_MASTERY_GRACE = 271534, + SPELL_PRIEST_MIND_BLAST = 8092, SPELL_PRIEST_MIND_DEVOURER = 373202, SPELL_PRIEST_MIND_DEVOURER_AURA = 373204, SPELL_PRIEST_MINDBENDER_DISC = 123040, @@ -163,6 +180,7 @@ enum PriestSpells SPELL_PRIEST_PRAYER_OF_MENDING_AURA = 41635, SPELL_PRIEST_PRAYER_OF_MENDING_HEAL = 33110, SPELL_PRIEST_PRAYER_OF_MENDING_JUMP = 155793, + SPELL_PRIEST_PRAYERFUL_LITANY = 391209, SPELL_PRIEST_PROTECTIVE_LIGHT_AURA = 193065, SPELL_PRIEST_PROTECTOR_OF_THE_FRAIL = 373035, SPELL_PRIEST_PURGE_THE_WICKED = 204197, @@ -217,12 +235,19 @@ enum PriestSpells SPELL_PRIEST_VAMPIRIC_TOUCH = 34914, SPELL_PRIEST_VOID_SHIELD = 199144, SPELL_PRIEST_VOID_SHIELD_EFFECT = 199145, + SPELL_PRIEST_VOICE_OF_HARMONY = 390994, + SPELL_PRIEST_VOID_TORRENT = 263165, SPELL_PRIEST_WEAKENED_SOUL = 6788, SPELL_PRIEST_WHISPERING_SHADOWS = 406777, SPELL_PRIEST_WHISPERING_SHADOWS_DUMMY = 391286, SPELL_PVP_RULES_ENABLED_HARDCODED = 134735 }; +enum PriestSpellLabels +{ + SPELL_LABEL_PRIEST_APOTHEOSIS = 3635, +}; + enum PriestSpellVisuals { SPELL_VISUAL_PRIEST_POWER_WORD_RADIANCE = 52872, @@ -716,6 +741,38 @@ class spell_pri_benediction : public SpellScript } }; +// 368275 - Binding Heals +class spell_pri_binding_heals : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_BINDING_HEALS_HEAL }); + } + + static bool CheckProc(AuraScript const&, ProcEventInfo const& eventInfo) + { + return eventInfo.GetActor() != eventInfo.GetProcTarget(); + } + + static void HandleEffectProc(AuraScript const&, AuraEffect const* aurEff, ProcEventInfo const& eventInfo) + { + Unit* caster = eventInfo.GetActor(); + + caster->CastSpell(caster, SPELL_PRIEST_BINDING_HEALS_HEAL, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = eventInfo.GetProcSpell(), + .TriggeringAura = aurEff, + .SpellValueOverrides = { { SPELLVALUE_BASE_POINT0, int32(CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount())) } } + }); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pri_binding_heals::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_binding_heals::HandleEffectProc, EFFECT_0, SPELL_AURA_DUMMY); + } +}; + // 215768 - Blaze of Light class spell_pri_blaze_of_light : public AuraScript { @@ -746,6 +803,30 @@ class spell_pri_blaze_of_light : public AuraScript } }; +// 372307 - Burning Vehemence +class spell_pri_burning_vehemence : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_BURNING_VEHEMENCE_DAMAGE }); + } + + void HandleEffectProc(AuraEffect const* aurEff, ProcEventInfo const& eventInfo) const + { + GetTarget()->CastSpell(eventInfo.GetActionTarget()->GetPosition(), SPELL_PRIEST_BURNING_VEHEMENCE_DAMAGE, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = eventInfo.GetProcSpell(), + .TriggeringAura = aurEff, + .SpellValueOverrides = { { SPELLVALUE_BASE_POINT0, int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount())) } } + }); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_burning_vehemence::HandleEffectProc, EFFECT_2, SPELL_AURA_DUMMY); + } +}; + // 204883 - Circle of Healing class spell_pri_circle_of_healing : public SpellScript { @@ -1258,6 +1339,39 @@ private: std::vector<ObjectGuid> _affectedUnits; }; +// 1215241 - Divinity +class spell_pri_divinity : public AuraScript +{ + bool Validate(SpellInfo const* spellInfo) override + { + return ValidateSpellEffect({ { spellInfo->Id, EFFECT_2 } }) + && ValidateSpellInfo({ SPELL_PRIEST_APOTHEOSIS, spellInfo->GetEffect(EFFECT_2).TriggerSpell }); + } + + static bool CheckProc(AuraScript const&, ProcEventInfo const& eventInfo) + { + return eventInfo.GetSpellInfo()->HasLabel(SPELL_LABEL_PRIEST_APOTHEOSIS); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& procInfo) + { + PreventDefaultAction(); + + GetTarget()->CastSpell(GetTarget(), aurEff->GetSpellEffectInfo().TriggerSpell, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = procInfo.GetProcSpell(), + .TriggeringAura = aurEff, + .SpellValueOverrides = { { SPELLVALUE_AURA_STACK, int32(sSpellMgr->AssertSpellInfo(aurEff->GetSpellEffectInfo().TriggerSpell, DIFFICULTY_NONE)->StackAmount) } } + }); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pri_divinity::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_divinity::HandleProc, EFFECT_2, SPELL_AURA_PROC_TRIGGER_SPELL); + } +}; + struct spell_pri_holy_words_base { static void ModifyCooldown(Unit* priest, SpellInfo const* spellInfo, Milliseconds cooldownMod) @@ -1309,6 +1423,196 @@ class spell_pri_empowered_renew_heal : public AuraScript } }; +// 447444 - Entropic Rift +// Triggered by 8092 - Mind Blast (Discipline) and 263165 - Void Torrent (Shadow) +class spell_pri_entropic_rift : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_ENTROPIC_RIFT }); + } + + bool Load() override + { + Unit* caster = GetCaster(); + return caster->HasAura(SPELL_PRIEST_ENTROPIC_RIFT) + && caster->IsPlayer() + && GetSpellInfo()->Id == uint32(caster->ToPlayer()->GetPrimarySpecialization() == ChrSpecialization::PriestShadow + ? SPELL_PRIEST_VOID_TORRENT + : SPELL_PRIEST_MIND_BLAST); + } + + void HandleEffectHit(SpellEffIndex /*effIndex*/) const + { + Unit* target = GetHitUnit(); + + GetCaster()->CastSpell(target->GetPosition(), SPELL_PRIEST_ENTROPIC_RIFT_AREATRIGGER, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .CustomArg = target->GetGUID() + }); + } + + void Register() override + { + if (m_scriptSpellId == SPELL_PRIEST_MIND_BLAST) + OnEffectHitTarget += SpellEffectFn(spell_pri_entropic_rift::HandleEffectHit, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + else + OnEffectHitTarget += SpellEffectFn(spell_pri_entropic_rift::HandleEffectHit, EFFECT_0, SPELL_EFFECT_APPLY_AURA); + } +}; + +// 450193 - Entropic Rift (Aura) +class spell_pri_entropic_rift_aura : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_ENTROPIC_RIFT_AREATRIGGER }); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) const + { + if (AreaTrigger* at = GetTarget()->GetAreaTrigger(SPELL_PRIEST_ENTROPIC_RIFT_AREATRIGGER)) + at->Remove(); + } + + void Register() override + { + OnEffectRemove += AuraEffectApplyFn(spell_pri_entropic_rift_aura::HandleRemove, EFFECT_0, SPELL_AURA_MOD_SPEED_ALWAYS, AURA_EFFECT_HANDLE_REAL); + } +}; + +// 459314 - Entropic Rift (Periodic) +class spell_pri_entropic_rift_periodic : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_ENTROPIC_RIFT_AREATRIGGER, SPELL_PRIEST_ENTROPIC_RIFT_DAMAGE }); + } + + void HandlePeriodic(AuraEffect const* /*aurEff*/) const + { + Unit* caster = GetTarget(); + + AreaTrigger const* at = caster->GetAreaTrigger(SPELL_PRIEST_ENTROPIC_RIFT_AREATRIGGER); + if (!at) + return; + + SpellInfo const* damageSpell = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_ENTROPIC_RIFT_DAMAGE, GetCastDifficulty()); + + for (ObjectGuid const& unitInAreaTrigger : at->GetInsideUnits()) + if (Unit* target = ObjectAccessor::GetUnit(*at, unitInAreaTrigger)) + if (caster->IsValidAttackTarget(target, damageSpell)) + caster->CastSpell(target, SPELL_PRIEST_ENTROPIC_RIFT_DAMAGE, TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_pri_entropic_rift_periodic::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } +}; + +// 447445 - Entropic Rift (AreaTrigger) +struct areatrigger_pri_entropic_rift : public AreaTriggerAI +{ + using AreaTriggerAI::AreaTriggerAI; + + static constexpr std::array<DBCPosition2D, 2> OverrideScaleCurve = + {{ + { .X = 0.0f, .Y = 1.0f }, + { .X = 1.0f, .Y = 1.0f }, + }}; + + void OnCreate(Spell const* creatingSpell) override + { + Unit* caster = at->GetCaster(); + if (!caster) + return; + + CastSpellExtraArgs args; + args.TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR; + + if (creatingSpell) + { + args.OriginalCastId = creatingSpell->m_castId; + + if (ObjectGuid const* targetGUID = std::any_cast<ObjectGuid>(&creatingSpell->m_customArg)) + at->SetPathTarget(*targetGUID); + + _searchRadius = creatingSpell->GetSpellInfo()->GetMaxRange(); + } + + caster->CastSpell(caster, SPELL_PRIEST_ENTROPIC_RIFT_AURA, args); + caster->CastSpell(caster, SPELL_PRIEST_ENTROPIC_RIFT_PERIODIC, args); + + UpdateMovement(); + _scheduler.Schedule(500ms, [this](TaskContext task) + { + UpdateMovement(); + task.Repeat(500ms); + }); + } + + void OnUpdate(uint32 diff) override + { + _scheduler.Update(diff); + } + + void OnDestinationReached() override + { + _movementSpeed = 7.0f; // Entropic Rift moves slower after reaching its target + } + + void UpdateMovement() + { + at->SetOverrideScaleCurve(OverrideScaleCurve); // updates StartTimeOffset of the curve + + Unit* target = UpdateTarget(); + if (!target) + return; + + at->SetPathTarget(target->GetGUID()); + + if (at->IsInDist2d(target, 0.5f)) + return; + + PathGenerator path(at); + path.CalculatePath(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), false); + at->InitSplines(path.GetPath(), _movementSpeed); + } + + Unit* UpdateTarget() const + { + SpellInfo const* damageSpell = sSpellMgr->GetSpellInfo(SPELL_PRIEST_ENTROPIC_RIFT_DAMAGE, DIFFICULTY_NONE); + if (!damageSpell || damageSpell->GetEffects().empty()) + return nullptr; + + Unit* caster = at->GetCaster(); + if (!caster) + return nullptr; + + SpellEffectInfo const& damageEffect = damageSpell->GetEffect(EFFECT_0); + Trinity::WorldObjectSpellAreaTargetCheck check(_searchRadius, caster, caster, caster, damageSpell, TARGET_CHECK_ENEMY, damageEffect.ImplicitTargetConditions.get(), TARGET_OBJECT_TYPE_UNIT); + + Unit* target = ObjectAccessor::GetUnit(*at, at->m_areaTriggerData->OrbitPathTarget); + if (!target || !check(target)) + { + std::vector<Unit*> targets; + Trinity::UnitListSearcher searcher(at, targets, check); + Spell::SearchTargets(searcher, GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER, caster, caster, _searchRadius); + Trinity::Containers::EraseIf(targets, [caster](Unit const* target) { return !caster->IsInCombatWith(target); }); + if (!targets.empty()) + target = Trinity::Containers::SelectRandomContainerElement(targets); + } + + return target; + } + +private: + TaskScheduler _scheduler; + float _movementSpeed = 12.0f; + float _searchRadius = 0.0f; +}; + // 414553 - Epiphany class spell_pri_epiphany : public AuraScript { @@ -1353,6 +1657,32 @@ class spell_pri_essence_devourer_heal : public SpellScript } }; +// 1215245 - Eternal Sanctity +class spell_pri_eternal_sanctity : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_APOTHEOSIS }); + } + + static bool CheckProc(AuraScript const&, ProcEventInfo const& eventInfo) + { + return eventInfo.GetActor()->HasAura(SPELL_PRIEST_APOTHEOSIS); + } + + static void HandleEffectProc(AuraScript const&, AuraEffect const* aurEff, ProcEventInfo const& eventInfo) + { + if (Aura* apotheosisAura = eventInfo.GetActor()->GetAura(SPELL_PRIEST_APOTHEOSIS)) + apotheosisAura->SetDuration(apotheosisAura->GetDuration() + aurEff->GetAmount()); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_pri_eternal_sanctity::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_eternal_sanctity::HandleEffectProc, EFFECT_0, SPELL_AURA_DUMMY); + } +}; + // 246287 - Evangelism class spell_pri_evangelism : public SpellScript { @@ -1523,6 +1853,56 @@ class spell_pri_guardian_spirit : public AuraScript } }; +// 373180 - Harsh Discipline +// Triggered by 194509 - Power Word: Radiance +class spell_pri_harsh_discipline : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_HARSH_DISCIPLINE_AURA, SPELL_PRIEST_CASTIGATION }) + && ValidateSpellEffect({ { SPELL_PRIEST_HARSH_DISCIPLINE, EFFECT_1 }, { SPELL_PRIEST_PENANCE_CHANNEL_DAMAGE, EFFECT_1 } }); + } + + bool Load() override + { + return GetCaster()->HasAura(SPELL_PRIEST_HARSH_DISCIPLINE); + } + + void HandleEffectHit(SpellEffIndex /*effIndex*/) const + { + Unit* caster = GetCaster(); + SpellInfo const* penanceChannel = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_PENANCE_CHANNEL_DAMAGE, GetCastDifficulty()); + int32 additionalBolts = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_HARSH_DISCIPLINE, GetCastDifficulty())->GetEffect(EFFECT_1).CalcValue(caster); + + // do the calc here + float channelDuration = penanceChannel->GetDuration(); + float channelPeriod = penanceChannel->GetEffect(EFFECT_1).ApplyAuraPeriod; + + float baseBolts = channelDuration / channelPeriod; + if (caster->HasAura(SPELL_PRIEST_CASTIGATION)) + baseBolts += 1.0f; + + float basePeriod = channelDuration / baseBolts; + + float totalBolts = baseBolts + additionalBolts; + float newPeriod = channelDuration / totalBolts; + float pctDiff = GetPctOf(newPeriod - basePeriod, basePeriod); + + caster->CastSpell(caster, SPELL_PRIEST_HARSH_DISCIPLINE_AURA, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .SpellValueOverrides = { + { SPELLVALUE_BASE_POINT0, static_cast<int32>(std::floor(pctDiff)) }, + { SPELLVALUE_BASE_POINT1, additionalBolts } + } + }); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_pri_harsh_discipline::HandleEffectHit, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + // 421558 - Heaven's Wrath class spell_pri_heavens_wrath : public AuraScript { @@ -1681,6 +2061,36 @@ class spell_pri_holy_words : public AuraScript } }; +// 88625 - Holy Word: Chastise +class spell_pri_holy_word_chastise : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo + ({ + SPELL_PRIEST_HOLY_WORD_CHASTISE_INCAPACITATE, + SPELL_PRIEST_HOLY_WORD_CHASTISE_STUN, + SPELL_PRIEST_CENSURE + }); + } + + void HandleAfterHit() const + { + Unit* caster = GetCaster(); + uint32 spellId = caster->HasAura(SPELL_PRIEST_CENSURE) ? SPELL_PRIEST_HOLY_WORD_CHASTISE_STUN : SPELL_PRIEST_HOLY_WORD_CHASTISE_INCAPACITATE; + + caster->CastSpell(GetHitUnit(), spellId, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell() + }); + } + + void Register() override + { + AfterHit += SpellHitFn(spell_pri_holy_word_chastise::HandleAfterHit); + } +}; + // 265202 - Holy Word: Salvation class spell_pri_holy_word_salvation : public SpellScript { @@ -1785,6 +2195,47 @@ class spell_pri_item_t6_trinket : public AuraScript } }; +// 471504 - Lasting Words +// Triggered by 2050 - Holy Word: Serenity and 34861 - Holy Word: Sanctify +class spell_pri_lasting_words : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_PRIEST_RENEW }) + && ValidateSpellEffect({ { SPELL_PRIEST_LASTING_WORDS, _spellEff } }); + } + + bool Load() override + { + return GetCaster()->HasAuraEffect(SPELL_PRIEST_LASTING_WORDS, _spellEff); + } + + void HandleEffectHit(SpellEffIndex /*effIndex*/) const + { + Unit* caster = GetCaster(); + + AuraEffect const* lastingWordsEff = caster->GetAuraEffect(SPELL_PRIEST_LASTING_WORDS, _spellEff); + if (!lastingWordsEff) + return; + + caster->CastSpell(GetHitUnit(), SPELL_PRIEST_RENEW, CastSpellExtraArgsInit{ + .TriggerFlags = TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD | TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_DONT_REPORT_CAST_ERROR, + .TriggeringSpell = GetSpell(), + .SpellValueOverrides = { { SPELLVALUE_DURATION, lastingWordsEff->GetAmount() } } + }); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_pri_lasting_words::HandleEffectHit, EFFECT_0, SPELL_EFFECT_HEAL); + } + + SpellEffIndex _spellEff; + +public: + explicit spell_pri_lasting_words(SpellEffIndex spellEff) : _spellEff(spellEff) { } +}; + // 92833 - Leap of Faith class spell_pri_leap_of_faith_effect_trigger : public SpellScript { @@ -2692,6 +3143,32 @@ class spell_pri_prayer_of_mending_jump : public spell_pri_prayer_of_mending_Spel } }; +// 391209 - Prayerful Litany (attached to 596 - Prayer of Healing) +class spell_pri_prayerful_litany : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellEffect({ { SPELL_PRIEST_PRAYERFUL_LITANY, EFFECT_0 } }); + } + + bool Load() override + { + return GetCaster()->HasAuraEffect(SPELL_PRIEST_PRAYERFUL_LITANY, EFFECT_0); + } + + void CalcPrimaryTargetHealing(SpellEffectInfo const& /*effectInfo*/, Unit const* victim, int32& /*healing*/, int32& /*flatMod*/, float& pctMod) const + { + if (victim == GetExplTargetUnit()) + if (AuraEffect const* prayerfulLitanyEff = GetCaster()->GetAuraEffect(SPELL_PRIEST_PRAYERFUL_LITANY, EFFECT_0)) + AddPct(pctMod, prayerfulLitanyEff->GetAmount()); + } + + void Register() override + { + CalcHealing += SpellCalcHealingFn(spell_pri_prayerful_litany::CalcPrimaryTargetHealing); + } +}; + // 193063 - Protective Light (Aura) class spell_pri_protective_light : public AuraScript { @@ -3441,7 +3918,8 @@ class spell_pri_t10_heal_2p_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_PRIEST_BLESSED_HEALING }); + return ValidateSpellEffect({ { SPELL_PRIEST_BLESSED_HEALING, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_PRIEST_BLESSED_HEALING, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& eventInfo) @@ -3452,11 +3930,10 @@ class spell_pri_t10_heal_2p_bonus : public AuraScript if (!healInfo || !healInfo->GetHeal()) return; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_BLESSED_HEALING, GetCastDifficulty()); + SpellEffectInfo const& hotEffect = sSpellMgr->AssertSpellInfo(SPELL_PRIEST_BLESSED_HEALING, GetCastDifficulty())->GetEffect(EFFECT_0); int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); - ASSERT(spellInfo->GetMaxTicks() > 0); - amount /= spellInfo->GetMaxTicks(); + amount /= hotEffect.GetPeriodicTickCount(); Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); @@ -3950,6 +4427,58 @@ class spell_pri_vampiric_touch : public AuraScript } }; +// 390994 - Voice of Harmony +class spell_pri_voice_of_harmony : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo + ({ + SPELL_PRIEST_HOLY_WORD_CHASTISE, + SPELL_PRIEST_HOLY_WORD_SANCTIFY, + SPELL_PRIEST_HOLY_WORD_SERENITY + }); + } + + static bool CheckHolyWordSanctify(AuraScript const&, AuraEffect const* /*aurEff*/, ProcEventInfo const& eventInfo) + { + // Divine Star + // Halo + return eventInfo.GetSpellInfo()->IsAffected(SPELLFAMILY_PRIEST, { 0x0, 0x0, 0x0, 0x4040 }); + } + + static bool CheckHolyWordSerenity(AuraScript const&, AuraEffect const* /*aurEff*/, ProcEventInfo const& eventInfo) + { + // Power Word: Life + // Prayer of Mending + return eventInfo.GetSpellInfo()->IsAffected(SPELLFAMILY_PRIEST, { 0x0, 0x0, 0x8000, 0x400000 }); + } + + static bool CheckHolyWordChastise(AuraScript const&, AuraEffect const* /*aurEff*/, ProcEventInfo const& eventInfo) + { + // Holy Fire + return eventInfo.GetSpellInfo()->IsAffected(SPELLFAMILY_PRIEST, { 0x100000, 0x0, 0x0, 0x0 }); + } + + template <uint32 TargetSpellId> + void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& /*eventInfo*/) const + { + SpellInfo const* targetSpellInfo = sSpellMgr->AssertSpellInfo(TargetSpellId, GetCastDifficulty()); + int32 cdReduction = aurEff->GetAmount(); + spell_pri_holy_words_base::ModifyCooldown(GetTarget(), targetSpellInfo, Seconds(-cdReduction)); + } + + void Register() override + { + DoCheckEffectProc += AuraCheckEffectProcFn(spell_pri_voice_of_harmony::CheckHolyWordSanctify, EFFECT_0, SPELL_AURA_DUMMY); + DoCheckEffectProc += AuraCheckEffectProcFn(spell_pri_voice_of_harmony::CheckHolyWordSerenity, EFFECT_1, SPELL_AURA_DUMMY); + DoCheckEffectProc += AuraCheckEffectProcFn(spell_pri_voice_of_harmony::CheckHolyWordChastise, EFFECT_2, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_pri_voice_of_harmony::HandleProc<SPELL_PRIEST_HOLY_WORD_SANCTIFY>, EFFECT_0, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_pri_voice_of_harmony::HandleProc<SPELL_PRIEST_HOLY_WORD_SERENITY>, EFFECT_1, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_pri_voice_of_harmony::HandleProc<SPELL_PRIEST_HOLY_WORD_CHASTISE>, EFFECT_2, SPELL_AURA_DUMMY); + } +}; + // 205385 - Shadow Crash class spell_pri_whispering_shadows : public SpellScript { @@ -4035,7 +4564,9 @@ void AddSC_priest_spell_scripts() RegisterSpellScript(spell_pri_atonement_effect_aura); RegisterSpellScript(spell_pri_atonement_passive); RegisterSpellScript(spell_pri_benediction); + RegisterSpellScript(spell_pri_binding_heals); RegisterSpellScript(spell_pri_blaze_of_light); + RegisterSpellScript(spell_pri_burning_vehemence); RegisterSpellScript(spell_pri_circle_of_healing); RegisterSpellScript(spell_pri_crystalline_reflection); RegisterSpellScript(spell_pri_dark_indulgence); @@ -4048,11 +4579,17 @@ void AddSC_priest_spell_scripts() RegisterSpellScript(spell_pri_divine_service); RegisterSpellScript(spell_pri_divine_star_shadow); RegisterAreaTriggerAI(areatrigger_pri_divine_star); + RegisterSpellScript(spell_pri_divinity); RegisterSpellScript(spell_pri_divine_procession); RegisterSpellScript(spell_pri_empowered_renew); RegisterSpellScript(spell_pri_empowered_renew_heal); + RegisterSpellScript(spell_pri_entropic_rift); + RegisterSpellScript(spell_pri_entropic_rift_aura); + RegisterSpellScript(spell_pri_entropic_rift_periodic); + RegisterAreaTriggerAI(areatrigger_pri_entropic_rift); RegisterSpellScript(spell_pri_epiphany); RegisterSpellScript(spell_pri_essence_devourer_heal); + RegisterSpellScript(spell_pri_eternal_sanctity); RegisterSpellScript(spell_pri_evangelism); RegisterSpellScript(spell_pri_expiation); RegisterSpellScript(spell_pri_focused_mending); @@ -4060,12 +4597,16 @@ void AddSC_priest_spell_scripts() RegisterSpellScript(spell_pri_guardian_spirit); RegisterSpellScript(spell_pri_halo_shadow); RegisterAreaTriggerAI(areatrigger_pri_halo); + RegisterSpellScript(spell_pri_harsh_discipline); RegisterSpellScript(spell_pri_heavens_wrath); RegisterSpellScript(spell_pri_holy_mending); RegisterSpellScript(spell_pri_holy_words); + RegisterSpellScript(spell_pri_holy_word_chastise); RegisterSpellScript(spell_pri_holy_word_salvation); RegisterSpellScript(spell_pri_holy_word_salvation_cooldown_reduction); RegisterSpellScript(spell_pri_item_t6_trinket); + RegisterSpellScriptWithArgs(spell_pri_lasting_words, "spell_pri_lasting_words_serenity", EFFECT_0); + RegisterSpellScriptWithArgs(spell_pri_lasting_words, "spell_pri_lasting_words_sanctify", EFFECT_1); RegisterSpellScript(spell_pri_leap_of_faith_effect_trigger); RegisterSpellScript(spell_pri_levitate); RegisterSpellScript(spell_pri_lights_wrath); @@ -4088,6 +4629,7 @@ void AddSC_priest_spell_scripts() RegisterSpellScript(spell_pri_prayer_of_mending_dummy); RegisterSpellAndAuraScriptPair(spell_pri_prayer_of_mending, spell_pri_prayer_of_mending_aura); RegisterSpellScript(spell_pri_prayer_of_mending_jump); + RegisterSpellScript(spell_pri_prayerful_litany); RegisterSpellScript(spell_pri_protective_light); RegisterSpellScript(spell_pri_protector_of_the_frail); RegisterSpellScript(spell_pri_holy_10_1_class_set_2pc); @@ -4124,6 +4666,7 @@ void AddSC_priest_spell_scripts() RegisterSpellScript(spell_pri_vampiric_embrace); RegisterSpellScript(spell_pri_vampiric_embrace_target); RegisterSpellScript(spell_pri_vampiric_touch); + RegisterSpellScript(spell_pri_voice_of_harmony); RegisterSpellScript(spell_pri_whispering_shadows); RegisterSpellScript(spell_pri_whispering_shadows_effect); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index c8d37c85acd..f6cc4ee3569 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -2948,7 +2948,8 @@ class spell_sha_t8_elemental_4p_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_SHAMAN_ELECTRIFIED }); + return ValidateSpellEffect({ { SPELL_SHAMAN_ELECTRIFIED, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_ELECTRIFIED, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& eventInfo) @@ -2959,11 +2960,10 @@ class spell_sha_t8_elemental_4p_bonus : public AuraScript if (!damageInfo || !damageInfo->GetDamage()) return; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_ELECTRIFIED, GetCastDifficulty()); + SpellEffectInfo const& dotEffect = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_ELECTRIFIED, GetCastDifficulty())->GetEffect(EFFECT_0); int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); - ASSERT(spellInfo->GetMaxTicks() > 0); - amount /= spellInfo->GetMaxTicks(); + amount /= dotEffect.GetPeriodicTickCount(); Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); @@ -2986,7 +2986,8 @@ class spell_sha_t9_elemental_4p_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE }); + return ValidateSpellEffect({ { SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& eventInfo) @@ -2997,11 +2998,10 @@ class spell_sha_t9_elemental_4p_bonus : public AuraScript if (!damageInfo || !damageInfo->GetDamage()) return; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE, GetCastDifficulty()); + SpellEffectInfo const& dotEffect = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_LAVA_BURST_BONUS_DAMAGE, GetCastDifficulty())->GetEffect(EFFECT_0); int32 amount = CalculatePct(static_cast<int32>(damageInfo->GetDamage()), aurEff->GetAmount()); - ASSERT(spellInfo->GetMaxTicks() > 0); - amount /= spellInfo->GetMaxTicks(); + amount /= dotEffect.GetPeriodicTickCount(); Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); @@ -3056,7 +3056,8 @@ class spell_sha_t10_restoration_4p_bonus : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_SHAMAN_CHAINED_HEAL }); + return ValidateSpellEffect({ { SPELL_SHAMAN_CHAINED_HEAL, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_CHAINED_HEAL, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount() > 0; } void HandleProc(AuraEffect const* aurEff, ProcEventInfo const& eventInfo) @@ -3067,11 +3068,10 @@ class spell_sha_t10_restoration_4p_bonus : public AuraScript if (!healInfo || !healInfo->GetHeal()) return; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_CHAINED_HEAL, GetCastDifficulty()); + SpellEffectInfo const& dotEffect = sSpellMgr->AssertSpellInfo(SPELL_SHAMAN_CHAINED_HEAL, GetCastDifficulty())->GetEffect(EFFECT_0); int32 amount = CalculatePct(static_cast<int32>(healInfo->GetHeal()), aurEff->GetAmount()); - ASSERT(spellInfo->GetMaxTicks() > 0); - amount /= spellInfo->GetMaxTicks(); + amount /= dotEffect.GetPeriodicTickCount(); Unit* caster = eventInfo.GetActor(); Unit* target = eventInfo.GetProcTarget(); diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 53b48383ca9..a799262f624 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -1476,14 +1476,15 @@ class spell_warr_trauma : public AuraScript { bool Validate(SpellInfo const* /*spellInfo*/) override { - return ValidateSpellInfo({ SPELL_WARRIOR_TRAUMA_EFFECT }); + return ValidateSpellEffect({ { SPELL_WARRIOR_TRAUMA_EFFECT, EFFECT_0 } }) + && sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_TRAUMA_EFFECT, DIFFICULTY_NONE)->GetEffect(EFFECT_0).GetPeriodicTickCount(); } void HandleProc(AuraEffect* aurEff, ProcEventInfo& eventInfo) { Unit* target = eventInfo.GetActionTarget(); //Get 25% of damage from the spell casted (Slam & Whirlwind) plus Remaining Damage from Aura - int32 damage = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()) / sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_TRAUMA_EFFECT, GetCastDifficulty())->GetMaxTicks()); + int32 damage = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()) / sSpellMgr->AssertSpellInfo(SPELL_WARRIOR_TRAUMA_EFFECT, GetCastDifficulty())->GetEffect(EFFECT_0).GetPeriodicTickCount()); CastSpellExtraArgs args(TRIGGERED_FULL_MASK); args.AddSpellMod(SPELLVALUE_BASE_POINT0, damage); GetCaster()->CastSpell(target, SPELL_WARRIOR_TRAUMA_EFFECT, args); diff --git a/src/server/scripts/Zandalar/Underrot/boss_cragmaw_the_infested.cpp b/src/server/scripts/Zandalar/Underrot/boss_cragmaw_the_infested.cpp index 4c174e055a3..163cc81128c 100644 --- a/src/server/scripts/Zandalar/Underrot/boss_cragmaw_the_infested.cpp +++ b/src/server/scripts/Zandalar/Underrot/boss_cragmaw_the_infested.cpp @@ -155,7 +155,7 @@ struct boss_cragmaw_the_infested : public BossAI void MovementInform(uint32 /*type*/, uint32 id) override { if (id == POINT_TANTRUM_START_RND_MOVEMENT) - me->GetMotionMaster()->MoveRandom(20.0f); + me->GetMotionMaster()->MoveRandom(20.0f, {}, {}, MovementWalkRunSpeedSelectionMode::ForceRun); } void OnChannelFinished(SpellInfo const* spell) override diff --git a/src/server/shared/Packets/ByteBuffer.h b/src/server/shared/Packets/ByteBuffer.h index df6fc935a58..b388e8b888c 100644 --- a/src/server/shared/Packets/ByteBuffer.h +++ b/src/server/shared/Packets/ByteBuffer.h @@ -619,9 +619,9 @@ class TC_SHARED_API ByteBuffer void hexlike() const; - protected: [[noreturn]] void OnInvalidPosition(size_t pos, size_t valueSize) const; + protected: size_t _rpos, _wpos; uint8 _bitpos; uint8 _curbitval; |
