diff options
Diffstat (limited to 'src/server/game')
67 files changed, 2151 insertions, 1777 deletions
diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp index ec22e9e4eba..17e03495593 100644 --- a/src/server/game/AI/CreatureAISelector.cpp +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -131,12 +131,7 @@ namespace FactorySelector MovementGenerator* SelectMovementGenerator(Unit* unit) { - MovementGeneratorType type = unit->GetDefaultMovementType(); - if (Creature* creature = unit->ToCreature()) - if (!creature->GetPlayerMovingMe()) - type = creature->GetDefaultMovementType(); - - MovementGeneratorCreator const* mv_factory = sMovementGeneratorRegistry->GetRegistryItem(type); + MovementGeneratorCreator const* mv_factory = sMovementGeneratorRegistry->GetRegistryItem(unit->GetDefaultMovementType()); return ASSERT_NOTNULL(mv_factory)->Create(unit); } 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/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index adbc5a07bea..2b2d3bb2fd5 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -265,11 +265,11 @@ void LFGMgr::LoadLFGDungeons() continue; } - dungeon.map = at->target_mapId; - dungeon.x = at->target_X; - dungeon.y = at->target_Y; - dungeon.z = at->target_Z; - dungeon.o = at->target_Orientation; + dungeon.map = at->Loc.GetMapId(); + dungeon.x = at->Loc.GetPositionX(); + dungeon.y = at->Loc.GetPositionY(); + dungeon.z = at->Loc.GetPositionZ(); + dungeon.o = at->Loc.GetOrientation(); } if (dungeon.type != LFG_TYPE_RANDOM) 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..3ca8b75f929 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; @@ -2973,6 +2973,14 @@ bool Creature::CanSwim() const return false; } +MovementGeneratorType Creature::GetDefaultMovementType() const +{ + if (!GetPlayerMovingMe()) + return m_defaultMovementType; + + return IDLE_MOTION_TYPE; +} + void Creature::AllLootRemovedFromCorpse() { time_t now = GameTime::GetGameTime(); @@ -3297,7 +3305,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 +3327,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 +3876,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()); + *data << uint32(m_values.GetChangedObjectTypeMask()); - if (m_values.HasChanged(TYPEID_OBJECT)) - m_objectData->WriteUpdate(*data, flags, this, target); - - 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/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index bab44b0740b..7cecf8e1d3e 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -160,7 +160,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool CanEnterWater() const override { return (CanSwim() || IsAmphibious()); }; bool CanFly() const override { return (IsFlying() || HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)); } - MovementGeneratorType GetDefaultMovementType() const override { return m_defaultMovementType; } + MovementGeneratorType GetDefaultMovementType() const override; void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; } CreatureClassifications GetCreatureClassification() const { return GetCreatureTemplate()->Classification; } 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 48ed667e545..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 @@ -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 5a6782dcddb..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 { @@ -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..c5822c6fd69 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); @@ -18185,8 +18192,13 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol // NOW player must have valid map // load the player's map here if it's not already loaded + bool isNewMap = false; if (!map) + { map = sMapMgr->CreateMap(mapId, this); + isNewMap = true; + } + AreaTriggerTeleport const* areaTrigger = nullptr; bool check = false; @@ -18203,9 +18215,14 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol areaTrigger = sObjectMgr->GetGoBackTrigger(mapId); check = true; } - else if (instanceId && !sInstanceLockMgr.FindActiveInstanceLock(guid, { mapId, map->GetDifficultyID() })) // ... and instance is reseted then look for entrance. + else if (instanceId && isNewMap) // ... and instance is reseted then look for entrance. { - areaTrigger = sObjectMgr->GetMapEntranceTrigger(mapId); + if (InstanceScript const* instanceScript = map->ToInstanceMap()->GetInstanceScript()) + areaTrigger = sObjectMgr->GetWorldSafeLoc(instanceScript->GetEntranceLocation()); + + if (!areaTrigger) + areaTrigger = sObjectMgr->GetMapEntranceTrigger(mapId); + check = true; } } @@ -18214,10 +18231,10 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol { if (areaTrigger) // ... if we have an areatrigger, then relocate to new map/coordinates. { - Relocate(areaTrigger->target_X, areaTrigger->target_Y, areaTrigger->target_Z, GetOrientation()); - if (mapId != areaTrigger->target_mapId) + Relocate(areaTrigger->Loc); + if (mapId != areaTrigger->Loc.GetMapId()) { - mapId = areaTrigger->target_mapId; + mapId = areaTrigger->Loc.GetMapId(); map = sMapMgr->CreateMap(mapId, this); } } @@ -24097,7 +24114,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 cb39caa6db4..f51da1de0f8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -313,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; @@ -5902,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()); @@ -5952,6 +5953,7 @@ bool Unit::AttackStop() Unit* victim = m_attacking; + m_updateFlag.CombatVictim = false; m_attacking->_removeAttacker(this); m_attacking = nullptr; @@ -11201,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(); @@ -11217,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(); @@ -11233,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(); @@ -14074,7 +14079,7 @@ void Unit::SetPlayHoverAnim(bool enable, bool sendUpdate /*= true*/) if (IsPlayingHoverAnim() == enable) return; - _playHoverAnim = enable; + m_updateFlag.PlayHoverAnim = enable; if (!sendUpdate) return; @@ -14123,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 10e719d343a..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); } @@ -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/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index d55e44b774c..f4b6c19c6a9 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7137,10 +7137,7 @@ Trinity::IteratorPair<std::unordered_map<uint32, WorldSafeLocsEntry>::const_iter AreaTriggerTeleport const* ObjectMgr::GetAreaTrigger(uint32 trigger) const { - AreaTriggerContainer::const_iterator itr = _areaTriggerStore.find(trigger); - if (itr != _areaTriggerStore.end()) - return &itr->second; - return nullptr; + return Trinity::Containers::MapGetValuePtr(_areaTriggerStore, trigger); } AccessRequirement const* ObjectMgr::GetAccessRequirement(uint32 mapid, Difficulty difficulty) const @@ -7239,13 +7236,7 @@ void ObjectMgr::LoadAreaTriggerTeleports() continue; } - AreaTriggerTeleport& at = _areaTriggerStore[Trigger_ID]; - - at.target_mapId = portLoc->Loc.GetMapId(); - at.target_X = portLoc->Loc.GetPositionX(); - at.target_Y = portLoc->Loc.GetPositionY(); - at.target_Z = portLoc->Loc.GetPositionZ(); - at.target_Orientation = portLoc->Loc.GetOrientation(); + _areaTriggerStore[Trigger_ID] = portLoc; } while (result->NextRow()); @@ -7393,11 +7384,11 @@ AreaTriggerTeleport const* ObjectMgr::GetGoBackTrigger(uint32 Map) const uint32 entrance_map = parentId.value_or(mapEntry->CorpseMapID); for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr) { - if (itr->second.target_mapId == entrance_map) + if (itr->second->Loc.GetMapId() == entrance_map) { AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first); if (atEntry && atEntry->ContinentID == Map) - return &itr->second; + return itr->second; } } return nullptr; @@ -7410,11 +7401,10 @@ AreaTriggerTeleport const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const { for (AreaTriggerContainer::const_iterator itr = _areaTriggerStore.begin(); itr != _areaTriggerStore.end(); ++itr) { - if (itr->second.target_mapId == Map) + if (itr->second->Loc.GetMapId() == Map) { - AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(itr->first); - if (atEntry) - return &itr->second; + if (sAreaTriggerStore.HasRecord(itr->first)) + return itr->second; } } return nullptr; diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 15a59853a1d..66802e67363 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -449,14 +449,7 @@ struct TC_GAME_API SpellClickInfo typedef std::multimap<uint32, SpellClickInfo> SpellClickInfoContainer; -struct AreaTriggerTeleport -{ - uint32 target_mapId; - float target_X; - float target_Y; - float target_Z; - float target_Orientation; -}; +using AreaTriggerTeleport = WorldSafeLocsEntry; struct AreaTriggerPolygon { @@ -986,7 +979,7 @@ class TC_GAME_API ObjectMgr typedef std::unordered_map<uint32, Trinity::unique_trackable_ptr<Quest>> QuestContainer; typedef std::unordered_map<uint32 /*questObjectiveId*/, QuestObjective const*> QuestObjectivesByIdContainer; - typedef std::unordered_map<uint32, AreaTriggerTeleport> AreaTriggerContainer; + typedef std::unordered_map<uint32, AreaTriggerTeleport const*> AreaTriggerContainer; typedef std::unordered_map<uint32, uint32> AreaTriggerScriptContainer; diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 6c0505fca7e..dcca524b3df 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1273,9 +1273,8 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder const& holder) if (!pCurrChar->GetMap()->AddPlayerToMap(pCurrChar)) { - AreaTriggerTeleport const* at = sObjectMgr->GetGoBackTrigger(pCurrChar->GetMapId()); - if (at) - pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation()); + if (AreaTriggerTeleport const* at = sObjectMgr->GetGoBackTrigger(pCurrChar->GetMapId())) + pCurrChar->TeleportTo(at->Loc); else pCurrChar->TeleportTo(pCurrChar->m_homebind); } diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index fab4f104900..8087463d58f 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -599,7 +599,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge return; bool teleported = false; - if (player->GetMapId() != at->target_mapId) + if (player->GetMapId() != at->Loc.GetMapId()) { if (!player->IsAlive()) { @@ -609,7 +609,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge uint32 corpseMap = player->GetCorpseLocation().GetMapId(); do { - if (corpseMap == at->target_mapId) + if (corpseMap == at->Loc.GetMapId()) break; InstanceTemplate const* corpseInstance = sObjectMgr->GetInstanceTemplate(corpseMap); @@ -622,52 +622,52 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge return; } - TC_LOG_DEBUG("maps", "MAP: Player '{}' has corpse in instance {} and can enter.", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' has corpse in instance {} and can enter.", player->GetName(), at->Loc.GetMapId()); } else TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '{}' is dead but does not have a corpse!", player->GetName()); } - if (TransferAbortParams denyReason = Map::PlayerCannotEnter(at->target_mapId, player)) + if (TransferAbortParams denyReason = Map::PlayerCannotEnter(at->Loc.GetMapId(), player)) { switch (denyReason.Reason) { case TRANSFER_ABORT_MAP_NOT_ALLOWED: - TC_LOG_DEBUG("maps", "MAP: Player '{}' attempted to enter map with id {} which has no entry", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' attempted to enter map with id {} which has no entry", player->GetName(), at->Loc.GetMapId()); break; case TRANSFER_ABORT_DIFFICULTY: - TC_LOG_DEBUG("maps", "MAP: Player '{}' attempted to enter instance map {} but the requested difficulty was not found", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' attempted to enter instance map {} but the requested difficulty was not found", player->GetName(), at->Loc.GetMapId()); break; case TRANSFER_ABORT_NEED_GROUP: - TC_LOG_DEBUG("maps", "MAP: Player '{}' must be in a raid group to enter map {}", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' must be in a raid group to enter map {}", player->GetName(), at->Loc.GetMapId()); player->SendRaidGroupOnlyMessage(RAID_GROUP_ERR_ONLY, 0); break; case TRANSFER_ABORT_LOCKED_TO_DIFFERENT_INSTANCE: - TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because their permanent bind is incompatible with their group's", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because their permanent bind is incompatible with their group's", player->GetName(), at->Loc.GetMapId()); break; case TRANSFER_ABORT_ALREADY_COMPLETED_ENCOUNTER: - TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because their permanent bind is incompatible with their group's", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because their permanent bind is incompatible with their group's", player->GetName(), at->Loc.GetMapId()); break; case TRANSFER_ABORT_TOO_MANY_INSTANCES: - TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because he has exceeded the maximum number of instances per hour.", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because he has exceeded the maximum number of instances per hour.", player->GetName(), at->Loc.GetMapId()); break; case TRANSFER_ABORT_MAX_PLAYERS: break; case TRANSFER_ABORT_ZONE_IN_COMBAT: break; case TRANSFER_ABORT_NOT_FOUND: - TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because instance is resetting.", player->GetName(), at->target_mapId); + TC_LOG_DEBUG("maps", "MAP: Player '{}' cannot enter instance map {} because instance is resetting.", player->GetName(), at->Loc.GetMapId()); break; default: break; } if (denyReason.Reason != TRANSFER_ABORT_NEED_GROUP) - player->SendTransferAborted(at->target_mapId, denyReason.Reason, denyReason.Arg, denyReason.MapDifficultyXConditionId); + player->SendTransferAborted(at->Loc.GetMapId(), denyReason.Reason, denyReason.Arg, denyReason.MapDifficultyXConditionId); if (!player->IsAlive() && player->HasCorpse()) { - if (player->GetCorpseLocation().GetMapId() == at->target_mapId) + if (player->GetCorpseLocation().GetMapId() == at->Loc.GetMapId()) { player->ResurrectPlayer(0.5f); player->SpawnCorpseBones(); @@ -684,11 +684,11 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::AreaTrigger::AreaTrigge if (!teleported) { - WorldSafeLocsEntry const* entranceLocation = player->GetInstanceEntrance(at->target_mapId); - if (entranceLocation && player->GetMapId() != at->target_mapId) + WorldSafeLocsEntry const* entranceLocation = player->GetInstanceEntrance(at->Loc.GetMapId()); + if (entranceLocation && player->GetMapId() != at->Loc.GetMapId()) player->TeleportTo(entranceLocation->Loc, TELE_TO_NOT_LEAVE_TRANSPORT); else - player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT); + player->TeleportTo(at->Loc, TELE_TO_NOT_LEAVE_TRANSPORT); } } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index c886f41ae44..b98d309346d 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); @@ -2907,6 +2907,10 @@ TransferAbortParams InstanceMap::CannotEnter(Player* player) return lockError; } + if (Group* owningGroup = GetOwningGroup()) + if (!player->IsInGroup(owningGroup->GetGUID())) + return TRANSFER_ABORT_MAX_PLAYERS; + return Map::CannotEnter(player); } 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..bf2d2669d7d 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*/, @@ -804,10 +803,6 @@ void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_C void MotionMaster::MoveKnockbackFrom(Position const& origin, float speedXY, float speedZ, float angle /*= M_PI*/, Movement::SpellEffectExtraData const* spellEffectExtraData /*= nullptr*/) { - // This function may make players fall below map - if (_owner->GetTypeId() == TYPEID_PLAYER) - return; - if (std::abs(speedXY) < 0.01f && std::abs(speedZ) < 0.01f) return; @@ -1145,8 +1140,13 @@ void MotionMaster::MovePath(uint32 pathId, bool repeatable, Optional<Millisecond TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePath: '{}', starts moving over path Id: {} (repeatable: {})", _owner->GetGUID(), pathId, repeatable ? "YES" : "NO"); - Add(new WaypointMovementGenerator<Creature>(pathId, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, - wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, std::move(scriptResult)), MOTION_SLOT_DEFAULT); + + if (_owner->GetTypeId() == TYPEID_UNIT) + Add(new WaypointMovementGenerator<Creature>(pathId, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, + wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, std::move(scriptResult)), MOTION_SLOT_DEFAULT); + else + Add(new WaypointMovementGenerator<Player>(pathId, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, + wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, std::move(scriptResult)), MOTION_SLOT_DEFAULT); } void MotionMaster::MovePath(WaypointPath const& path, bool repeatable, Optional<Milliseconds> duration /*= {}*/, Optional<float> speed /*= {}*/, @@ -1158,8 +1158,13 @@ void MotionMaster::MovePath(WaypointPath const& path, bool repeatable, Optional< { TC_LOG_DEBUG("movement.motionmaster", "MotionMaster::MovePath: '{}', starts moving over path Id: {} (repeatable: {})", _owner->GetGUID(), path.Id, repeatable ? "YES" : "NO"); - Add(new WaypointMovementGenerator<Creature>(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, - wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, std::move(scriptResult)), MOTION_SLOT_DEFAULT); + + if (_owner->GetTypeId() == TYPEID_UNIT) + Add(new WaypointMovementGenerator<Creature>(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, + wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, std::move(scriptResult)), MOTION_SLOT_DEFAULT); + else + Add(new WaypointMovementGenerator<Player>(path, repeatable, duration, speed, speedSelectionMode, waitTimeRangeAtPathEnd, + wanderDistanceAtPathEnds, followPathBackwardsFromEndToStart, exactSplinePath, generatePath, std::move(scriptResult)), MOTION_SLOT_DEFAULT); } void MotionMaster::MoveRotate(uint32 id, RotateDirection direction, Optional<Milliseconds> time /*= {}*/, 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/FlightPathMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h index ada9c742781..82969b337ca 100644 --- a/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h @@ -30,7 +30,7 @@ struct TaxiPathNodeEntry; * FlightPathMovementGenerator generates movement of the player for the paths * and hence generates ground and activities for the player. */ -class FlightPathMovementGenerator : public MovementGeneratorMedium<Player, FlightPathMovementGenerator>, public PathMovementBase<Player, std::vector<TaxiPathNodeEntry const*>> +class FlightPathMovementGenerator : public MovementGeneratorMedium<Player, FlightPathMovementGenerator>, public PathMovementBase<std::vector<TaxiPathNodeEntry const*>> { public: explicit FlightPathMovementGenerator(Optional<float> speed, diff --git a/src/server/game/Movement/MovementGenerators/PathMovementBase.h b/src/server/game/Movement/MovementGenerators/PathMovementBase.h index 7fe02bd100f..cdb2bdcbe20 100644 --- a/src/server/game/Movement/MovementGenerators/PathMovementBase.h +++ b/src/server/game/Movement/MovementGenerators/PathMovementBase.h @@ -21,7 +21,7 @@ #include "Define.h" #include <string> -template<class Entity, class BasePath> +template<class BasePath> class PathMovementBase { public: 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..aacaca8c950 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -25,6 +25,7 @@ #include "MoveSplineInit.h" #include "MovementDefines.h" #include "PathGenerator.h" +#include "Player.h" #include "Transport.h" #include "WaypointManager.h" #include <span> @@ -34,72 +35,78 @@ namespace constexpr Milliseconds SEND_NEXT_POINT_EARLY_DELTA = 1500ms; } -WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId, bool repeating, Optional<Milliseconds> duration, Optional<float> speed, +template <typename T> +WaypointMovementGenerator<T>::WaypointMovementGenerator(uint32 pathId, bool repeating, Optional<Milliseconds> duration, Optional<float> speed, MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd, Optional<float> wanderDistanceAtPathEnds, Optional<bool> followPathBackwardsFromEndToStart, Optional<bool> exactSplinePath, bool generatePath, Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult /*= {}*/) - : PathMovementBase(PathType(std::in_place_type<WaypointPath const*>)), _pathId(pathId), _speed(speed), _speedSelectionMode(speedSelectionMode), + : PathMovementBase(sWaypointMgr->GetPath(pathId)), _speed(speed), _speedSelectionMode(speedSelectionMode), _waitTimeRangeAtPathEnd(std::move(waitTimeRangeAtPathEnd)), _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds), _followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _exactSplinePath(exactSplinePath), _repeating(repeating), _generatePath(generatePath), _moveTimer(0), _nextMoveTime(0), _waypointTransitionSplinePointsIndex(0), _isReturningToStart(false) { - Mode = MOTION_MODE_DEFAULT; - Priority = MOTION_PRIORITY_NORMAL; - Flags = MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING; - BaseUnitState = UNIT_STATE_ROAMING; - ScriptResult = std::move(scriptResult); + this->Mode = MOTION_MODE_DEFAULT; + this->Priority = MOTION_PRIORITY_NORMAL; + this->Flags = MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING; + this->BaseUnitState = UNIT_STATE_ROAMING; + this->ScriptResult = std::move(scriptResult); if (duration) _duration.emplace(*duration); } -WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath const& path, bool repeating, Optional<Milliseconds> duration, Optional<float> speed, +template <typename T> +WaypointMovementGenerator<T>::WaypointMovementGenerator(WaypointPath const& path, bool repeating, Optional<Milliseconds> duration, Optional<float> speed, MovementWalkRunSpeedSelectionMode speedSelectionMode, Optional<std::pair<Milliseconds, Milliseconds>> waitTimeRangeAtPathEnd, Optional<float> wanderDistanceAtPathEnds, Optional<bool> followPathBackwardsFromEndToStart, Optional<bool> exactSplinePath, bool generatePath, Optional<Scripting::v2::ActionResultSetter<MovementStopReason>>&& scriptResult /*= {}*/) - : PathMovementBase(std::make_unique<WaypointPath>(path)), _pathId(0), _speed(speed), _speedSelectionMode(speedSelectionMode), + : PathMovementBase(std::make_unique<WaypointPath>(path)), _speed(speed), _speedSelectionMode(speedSelectionMode), _waitTimeRangeAtPathEnd(std::move(waitTimeRangeAtPathEnd)), _wanderDistanceAtPathEnds(wanderDistanceAtPathEnds), _followPathBackwardsFromEndToStart(followPathBackwardsFromEndToStart), _exactSplinePath(exactSplinePath), _repeating(repeating), _generatePath(generatePath), _moveTimer(0), _nextMoveTime(0), _waypointTransitionSplinePointsIndex(0), _isReturningToStart(false) { - Mode = MOTION_MODE_DEFAULT; - Priority = MOTION_PRIORITY_NORMAL; - Flags = MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING; - BaseUnitState = UNIT_STATE_ROAMING; - ScriptResult = std::move(scriptResult); + this->Mode = MOTION_MODE_DEFAULT; + this->Priority = MOTION_PRIORITY_NORMAL; + this->Flags = MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING; + this->BaseUnitState = UNIT_STATE_ROAMING; + this->ScriptResult = std::move(scriptResult); if (duration) _duration.emplace(*duration); - std::get<std::unique_ptr<WaypointPath>>(_path)->BuildSegments(); + std::get<std::unique_ptr<WaypointPath>>(this->_path)->BuildSegments(); } -WaypointMovementGenerator<Creature>::~WaypointMovementGenerator() = default; +template <typename T> +WaypointMovementGenerator<T>::~WaypointMovementGenerator() = default; -MovementGeneratorType WaypointMovementGenerator<Creature>::GetMovementGeneratorType() const +template <typename T> +MovementGeneratorType WaypointMovementGenerator<T>::GetMovementGeneratorType() const { return WAYPOINT_MOTION_TYPE; } -void WaypointMovementGenerator<Creature>::Pause(uint32 timer) +template <typename T> +void WaypointMovementGenerator<T>::Pause(uint32 timer) { if (timer) { // Don't try to paused an already paused generator - if (HasFlag(MOVEMENTGENERATOR_FLAG_PAUSED)) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_PAUSED)) return; - AddFlag(MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); _nextMoveTime.Reset(timer); - RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED); } else { - AddFlag(MOVEMENTGENERATOR_FLAG_PAUSED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_PAUSED); _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached - RemoveFlag(MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); } } -void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer) +template <typename T> +void WaypointMovementGenerator<T>::Resume(uint32 overrideTimer) { if (overrideTimer) _nextMoveTime.Reset(overrideTimer); @@ -107,10 +114,11 @@ void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer) if (_nextMoveTime.Passed()) _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached - RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_PAUSED); } -bool WaypointMovementGenerator<Creature>::GetResetPosition(Unit* /*owner*/, float& x, float& y, float& z) +template <typename T> +bool WaypointMovementGenerator<T>::GetResetPosition(Unit* /*owner*/, float& x, float& y, float& z) { // prevent a crash at empty waypoint path. WaypointPath const* path = GetPath(); @@ -126,22 +134,15 @@ bool WaypointMovementGenerator<Creature>::GetResetPosition(Unit* /*owner*/, floa return true; } -void WaypointMovementGenerator<Creature>::DoInitialize(Creature* owner) +template <typename T> +void WaypointMovementGenerator<T>::DoInitialize(T* owner) { - RemoveFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); - - if (IsLoadedFromDB()) - { - if (!_pathId) - _pathId = owner->GetWaypointPathId(); - - _path = sWaypointMgr->GetPath(_pathId); - } + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INITIALIZATION_PENDING | MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); WaypointPath const* path = GetPath(); if (!path) { - TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: couldn't load path for creature ({}) (_pathId: {})", owner->GetGUID(), _pathId); + TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::DoInitialize: couldn't load path for {}", owner->GetGUID()); return; } @@ -153,22 +154,24 @@ void WaypointMovementGenerator<Creature>::DoInitialize(Creature* owner) _nextMoveTime.Reset(1000); } -void WaypointMovementGenerator<Creature>::DoReset(Creature* owner) +template <typename T> +void WaypointMovementGenerator<T>::DoReset(T* owner) { - RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_DEACTIVATED); owner->StopMoving(); - if (!HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) && _nextMoveTime.Passed()) + if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) && _nextMoveTime.Passed()) _nextMoveTime.Reset(1); // Needed so that Update does not behave as if node was reached } -bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) +template <typename T> +bool WaypointMovementGenerator<T>::DoUpdate(T* 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; WaypointPath const* path = GetPath(); @@ -180,23 +183,25 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) _duration->Update(diff); if (_duration->Passed()) { - RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY); - AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); - AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); - owner->UpdateCurrentWaypointInfo(0, 0); - SetScriptResult(MovementStopReason::Finished); + if constexpr (std::is_base_of_v<Creature, T>) + owner->UpdateCurrentWaypointInfo(0, 0); + + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); + this->SetScriptResult(MovementStopReason::Finished); return false; } } if (owner->HasUnitState(UNIT_STATE_NOT_MOVE | UNIT_STATE_LOST_CONTROL) || owner->IsMovementPreventedByCasting()) { - AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); owner->StopMoving(); return true; } - if (HasFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED)) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED)) { /* * relaunch only if @@ -208,21 +213,22 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) * * TODO: ((_nextMoveTime.Passed() && VALID_MOVEMENT) || (!_nextMoveTime.Passed() && !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED))) */ - if (HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED) && (_nextMoveTime.Passed() || !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED))) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED) && (_nextMoveTime.Passed() || !this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED))) { StartMove(owner, true); return true; } - RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_INTERRUPTED); } // if it's moving if (!UpdateMoveTimer(diff) && !owner->movespline->Finalized()) { // set home position at place (every MotionMaster::UpdateMotion) - if (owner->GetTransGUID().IsEmpty()) - owner->SetHomePosition(owner->GetPosition()); + if constexpr (std::is_base_of_v<Creature, T>) + if (owner->GetTransGUID().IsEmpty()) + owner->SetHomePosition(owner->GetPosition()); // handle switching points in continuous segments if (IsExactSplinePath()) @@ -233,25 +239,28 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) OnArrived(owner); ++_waypointTransitionSplinePointsIndex; if (ComputeNextNode()) - if (CreatureAI* ai = owner->AI()) - ai->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); + { + if constexpr (std::is_base_of_v<Creature, T>) + if (CreatureAI* ai = owner->AI()) + ai->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); + } } } // relaunch movement if its speed has changed - if (HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING)) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING)) StartMove(owner, true); } else if (!_nextMoveTime.Passed()) // it's not moving, is there a timer? { if (UpdateWaitTimer(diff)) { - if (!HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) // initial movement call + if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) // initial movement call { StartMove(owner); return true; } - else if (!HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) // timer set before node was reached, resume now + else if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) // timer set before node was reached, resume now { StartMove(owner, true); return true; @@ -262,10 +271,10 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) } else // not moving, no timer { - if (HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED) && !HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED) && !this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED)) { OnArrived(owner); // hooks and wait timer reset (if necessary) - AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); // signals to future StartMove that it reached a node + this->AddFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED); // signals to future StartMove that it reached a node } if (_nextMoveTime.Passed()) // OnArrived might have set a timer @@ -275,27 +284,36 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* owner, uint32 diff) return true; } -void WaypointMovementGenerator<Creature>::DoDeactivate(Creature* owner) +template <typename T> +void WaypointMovementGenerator<T>::DoDeactivate(T* owner) { - AddFlag(MOVEMENTGENERATOR_FLAG_DEACTIVATED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_DEACTIVATED); owner->ClearUnitState(UNIT_STATE_ROAMING_MOVE); } -void WaypointMovementGenerator<Creature>::DoFinalize(Creature* owner, bool active, bool movementInform) +template <typename T> +void WaypointMovementGenerator<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); // TODO: Research if this modification is needed, which most likely isnt - owner->SetWalk(false); + if constexpr (std::is_base_of_v<Creature, T>) + owner->SetWalk(false); } if (movementInform) - SetScriptResult(MovementStopReason::Finished); + this->SetScriptResult(MovementStopReason::Finished); } +template <typename T> +void WaypointMovementGenerator<T>::MovementInform(T const* /*owner*/) const +{ +} + +template <> void WaypointMovementGenerator<Creature>::MovementInform(Creature const* owner) const { WaypointPath const* path = GetPath(); @@ -307,7 +325,8 @@ void WaypointMovementGenerator<Creature>::MovementInform(Creature const* owner) } } -void WaypointMovementGenerator<Creature>::OnArrived(Creature* owner) +template <typename T> +void WaypointMovementGenerator<T>::OnArrived(T* owner) { WaypointPath const* path = GetPath(); if (!path || path->Nodes.empty()) @@ -331,14 +350,15 @@ void WaypointMovementGenerator<Creature>::OnArrived(Creature* owner) _duration->Update(waitTime); // count the random movement time as part of waypoing movement action if (_wanderDistanceAtPathEnds) - owner->GetMotionMaster()->MoveRandom(*_wanderDistanceAtPathEnds, waitTime, MOTION_SLOT_ACTIVE); + owner->GetMotionMaster()->MoveRandom(*_wanderDistanceAtPathEnds, waitTime, _speed, _speedSelectionMode, MOTION_SLOT_ACTIVE); else _nextMoveTime.Reset(waitTime); } MovementInform(owner); - owner->UpdateCurrentWaypointInfo(waypoint.Id, path->Id); + if constexpr (std::is_base_of_v<Creature, T>) + owner->UpdateCurrentWaypointInfo(waypoint.Id, path->Id); } namespace @@ -441,73 +461,91 @@ void CreateMergedPath(Unit const* owner, WaypointPath const* path, uint32 previo } } -void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaunch/* = false*/) +template <typename T> +void WaypointMovementGenerator<T>::StartMove(T* owner, bool relaunch/* = false*/) { // sanity checks - if (!owner || !owner->IsAlive() || HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) || (relaunch && (HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) || !HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)))) + if (!owner->IsAlive() || this->HasFlag(MOVEMENTGENERATOR_FLAG_FINALIZED) + || (relaunch && (this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) || !this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)))) return; WaypointPath const* path = GetPath(); if (!path || path->Nodes.empty()) return; - if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting() || (owner->IsFormationLeader() && !owner->IsFormationLeaderMoveAllowed())) // if cannot move OR cannot move because of formation + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE) || owner->IsMovementPreventedByCasting()) // if cannot move { _nextMoveTime.Reset(1000); // delay 1s return; } + if constexpr (std::is_base_of_v<Creature, T>) + { + if (owner->IsFormationLeader() && !owner->IsFormationLeaderMoveAllowed()) // if cannot move because of formation + { + _nextMoveTime.Reset(1000); // delay 1s + return; + } + } + bool const transportPath = !owner->GetTransGUID().IsEmpty(); uint32 previousNode = _currentNode; - if (HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) && HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) + if (this->HasFlag(MOVEMENTGENERATOR_FLAG_INFORM_ENABLED) && this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) { if (ComputeNextNode()) { ASSERT(_currentNode < path->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->Id); // inform AI - if (CreatureAI* AI = owner->AI()) - AI->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); + if constexpr (std::is_base_of_v<Creature, T>) + if (CreatureAI* AI = owner->AI()) + AI->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); } else { - WaypointNode const& waypoint = path->Nodes[_currentNode]; - float x = waypoint.X; - float y = waypoint.Y; - float z = waypoint.Z; - float o = owner->GetOrientation(); - - if (!transportPath) - owner->SetHomePosition(x, y, z, o); - else + this->AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); + + if constexpr (std::is_base_of_v<Creature, T>) { - if (TransportBase* trans = owner->GetTransport()) + owner->UpdateCurrentWaypointInfo(0, 0); + + WaypointNode const& waypoint = path->Nodes[_currentNode]; + float x = waypoint.X; + float y = waypoint.Y; + float z = waypoint.Z; + float o = owner->GetOrientation(); + + if (!transportPath) + owner->SetHomePosition(x, y, z, o); + else { - o -= trans->GetTransportOrientation(); - owner->SetTransportHomePosition(x, y, z, o); - owner->SetHomePosition(trans->GetPositionWithOffset(owner->GetTransportHomePosition())); + if (TransportBase* trans = owner->GetTransport()) + { + o -= trans->GetTransportOrientation(); + owner->SetTransportHomePosition(x, y, z, o); + owner->SetHomePosition(trans->GetPositionWithOffset(owner->GetTransportHomePosition())); + } + // else if (vehicle) - this should never happen, vehicle offsets are const } - // else if (vehicle) - this should never happen, vehicle offsets are const - } - AddFlag(MOVEMENTGENERATOR_FLAG_FINALIZED); - owner->UpdateCurrentWaypointInfo(0, 0); - // inform AI - if (CreatureAI* AI = owner->AI()) - AI->WaypointPathEnded(waypoint.Id, path->Id); + // inform AI + if (CreatureAI* AI = owner->AI()) + AI->WaypointPathEnded(waypoint.Id, path->Id); + } - SetScriptResult(MovementStopReason::Finished); + this->SetScriptResult(MovementStopReason::Finished); return; } } - else if (!HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) + else if (!this->HasFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED)) { - AddFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED); + this->AddFlag(MOVEMENTGENERATOR_FLAG_INITIALIZED); // inform AI - if (CreatureAI* AI = owner->AI()) - AI->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); + if constexpr (std::is_base_of_v<Creature, T>) + if (CreatureAI* AI = owner->AI()) + AI->WaypointStarted(path->Nodes[_currentNode].Id, path->Id); } ASSERT(_currentNode < path->Nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, path->Id); @@ -524,7 +562,7 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun _waypointTransitionSplinePointsIndex = 0; - RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_INFORM_ENABLED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); + this->RemoveFlag(MOVEMENTGENERATOR_FLAG_TRANSITORY | MOVEMENTGENERATOR_FLAG_INFORM_ENABLED | MOVEMENTGENERATOR_FLAG_TIMED_PAUSED); owner->AddUnitState(UNIT_STATE_ROAMING_MOVE); @@ -554,7 +592,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); @@ -613,10 +651,12 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun _moveTimer.Reset(duration); // inform formation - owner->SignalFormationMovement(); + if constexpr (std::is_base_of_v<Creature, T>) + owner->SignalFormationMovement(); } -bool WaypointMovementGenerator<Creature>::ComputeNextNode() +template <typename T> +bool WaypointMovementGenerator<T>::ComputeNextNode() { WaypointPath const* path = GetPath(); if ((_currentNode == path->Nodes.size() - 1) && !_repeating) @@ -647,7 +687,8 @@ bool WaypointMovementGenerator<Creature>::ComputeNextNode() return true; } -bool WaypointMovementGenerator<Creature>::IsFollowingPathBackwardsFromEndToStart() const +template <typename T> +bool WaypointMovementGenerator<T>::IsFollowingPathBackwardsFromEndToStart() const { if (_followPathBackwardsFromEndToStart) return *_followPathBackwardsFromEndToStart; @@ -655,7 +696,8 @@ bool WaypointMovementGenerator<Creature>::IsFollowingPathBackwardsFromEndToStart return GetPath()->Flags.HasFlag(WaypointPathFlags::FollowPathBackwardsFromEndToStart); } -bool WaypointMovementGenerator<Creature>::IsExactSplinePath() const +template <typename T> +bool WaypointMovementGenerator<T>::IsExactSplinePath() const { if (_exactSplinePath) return *_exactSplinePath; @@ -663,7 +705,8 @@ bool WaypointMovementGenerator<Creature>::IsExactSplinePath() const return GetPath()->Flags.HasFlag(WaypointPathFlags::ExactSplinePath); } -bool WaypointMovementGenerator<Creature>::IsCyclic() const +template <typename T> +bool WaypointMovementGenerator<T>::IsCyclic() const { return !IsFollowingPathBackwardsFromEndToStart() && IsExactSplinePath() @@ -671,14 +714,18 @@ bool WaypointMovementGenerator<Creature>::IsCyclic() const && GetPath()->ContinuousSegments.size() == 1; } -std::string WaypointMovementGenerator<Creature>::GetDebugInfo() const +template <typename T> +std::string WaypointMovementGenerator<T>::GetDebugInfo() const { return Trinity::StringFormat("{}\n{}", PathMovementBase::GetDebugInfo(), - MovementGeneratorMedium::GetDebugInfo()); + MovementGeneratorMedium<T, WaypointMovementGenerator>::GetDebugInfo()); } -MovementGenerator* WaypointMovementFactory::Create(Unit* /*object*/) const +MovementGenerator* WaypointMovementFactory::Create(Unit* object) const { - return new WaypointMovementGenerator<Creature>(0, true); + return new WaypointMovementGenerator<Creature>(object->ToCreature()->GetWaypointPathId(), true); } + +template class WaypointMovementGenerator<Creature>; +template class WaypointMovementGenerator<Player>; diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index fb981f400cd..217687e12fb 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -24,15 +24,11 @@ #include "WaypointDefines.h" #include <variant> -class Creature; class Unit; -template<class T> -class WaypointMovementGenerator; - -template<> -class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creature, WaypointMovementGenerator<Creature>>, - public PathMovementBase<Creature, std::variant<WaypointPath const*, std::unique_ptr<WaypointPath>>> +template <typename T> +class WaypointMovementGenerator : public MovementGeneratorMedium<T, WaypointMovementGenerator<T>>, + public PathMovementBase<std::variant<WaypointPath const*, std::unique_ptr<WaypointPath>>> { public: explicit WaypointMovementGenerator(uint32 pathId, bool repeating, Optional<Milliseconds> duration = {}, Optional<float> speed = {}, @@ -49,25 +45,25 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat MovementGeneratorType GetMovementGeneratorType() const override; - void UnitSpeedChanged() override { AddFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING); } + void UnitSpeedChanged() override { this->AddFlag(MOVEMENTGENERATOR_FLAG_SPEED_UPDATE_PENDING); } void Pause(uint32 timer) override; void Resume(uint32 overrideTimer) override; bool GetResetPosition(Unit*, float& x, float& y, float& z) override; - void DoInitialize(Creature*); - void DoReset(Creature*); - bool DoUpdate(Creature*, uint32); - void DoDeactivate(Creature*); - void DoFinalize(Creature*, bool, bool); + void DoInitialize(T* owner); + void DoReset(T* owner); + bool DoUpdate(T* owner, uint32 diff); + void DoDeactivate(T* owner); + void DoFinalize(T* owner, bool active, bool movementInform); WaypointPath const* GetPath() const { return std::visit([](auto&& path) -> WaypointPath const* { return std::addressof(*path); }, _path); } std::string GetDebugInfo() const override; private: - void MovementInform(Creature const*) const; - void OnArrived(Creature*); - void StartMove(Creature*, bool relaunch = false); + void MovementInform(T const* owner) const; + void OnArrived(T* owner); + void StartMove(T* owner, bool relaunch = false); bool ComputeNextNode(); bool UpdateMoveTimer(uint32 diff) { return UpdateTimer(_moveTimer, diff); } bool UpdateWaitTimer(uint32 diff) { return UpdateTimer(_nextMoveTime, diff); } @@ -88,7 +84,6 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creat bool IsLoadedFromDB() const { return std::holds_alternative<WaypointPath const*>(_path); } - uint32 _pathId; Optional<TimeTracker> _duration; Optional<float> _speed; MovementWalkRunSpeedSelectionMode _speedSelectionMode; 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/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/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8cc94e2f59d..86e1d7f292a 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3624,8 +3624,6 @@ void Spell::cancel() SendChannelUpdate(0, SPELL_FAILED_INTERRUPTED); SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); - - m_appliedMods.clear(); break; default: break; @@ -5090,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)) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 185826c720b..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); @@ -3940,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) |
