aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/AI/CreatureAISelector.cpp7
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp3
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp10
-rw-r--r--src/server/game/Entities/Account/Account.cpp86
-rw-r--r--src/server/game/Entities/Account/Account.h54
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.cpp39
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.h2
-rw-r--r--src/server/game/Entities/Conversation/Conversation.cpp2
-rw-r--r--src/server/game/Entities/Corpse/Corpse.cpp6
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp40
-rw-r--r--src/server/game/Entities/Creature/Creature.h2
-rw-r--r--src/server/game/Entities/DynamicObject/DynamicObject.cpp2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp15
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h6
-rw-r--r--src/server/game/Entities/Item/Container/Bag.cpp2
-rw-r--r--src/server/game/Entities/Item/Item.cpp6
-rw-r--r--src/server/game/Entities/Item/Item.h4
-rw-r--r--src/server/game/Entities/Object/BaseEntity.cpp673
-rw-r--r--src/server/game/Entities/Object/BaseEntity.h419
-rw-r--r--src/server/game/Entities/Object/Object.cpp719
-rw-r--r--src/server/game/Entities/Object/Object.h509
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h11
-rw-r--r--src/server/game/Entities/Object/SmoothPhasing.cpp7
-rw-r--r--src/server/game/Entities/Object/SmoothPhasing.h7
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateData.cpp28
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateData.h26
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.cpp60
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.h67
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateMask.h10
-rw-r--r--src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.cpp48
-rw-r--r--src/server/game/Entities/Object/Updates/WowCSEntityDefinitions.h84
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp35
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Entities/SceneObject/SceneObject.cpp2
-rw-r--r--src/server/game/Entities/Transport/Transport.cpp4
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp11
-rw-r--r--src/server/game/Entities/Unit/Unit.h18
-rw-r--r--src/server/game/Entities/Vehicle/Vehicle.cpp12
-rw-r--r--src/server/game/Entities/Vehicle/Vehicle.h13
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp24
-rw-r--r--src/server/game/Globals/ObjectMgr.h11
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp5
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp32
-rw-r--r--src/server/game/Maps/Map.cpp6
-rw-r--r--src/server/game/Maps/Map.h15
-rw-r--r--src/server/game/Movement/MotionMaster.cpp35
-rw-r--r--src/server/game/Movement/MotionMaster.h3
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp63
-rw-r--r--src/server/game/Movement/MovementGenerators/FlightPathMovementGenerator.h2
-rw-r--r--src/server/game/Movement/MovementGenerators/PathMovementBase.h2
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp139
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h5
-rw-r--r--src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp271
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h29
-rw-r--r--src/server/game/Movement/Waypoints/WaypointDefines.h20
-rw-r--r--src/server/game/Movement/Waypoints/WaypointManager.cpp4
-rw-r--r--src/server/game/Quests/QuestDef.h1
-rw-r--r--src/server/game/Server/WorldSession.cpp28
-rw-r--r--src/server/game/Server/WorldSession.h24
-rw-r--r--src/server/game/Server/WorldSocket.cpp65
-rw-r--r--src/server/game/Spells/Spell.cpp4
-rw-r--r--src/server/game/Spells/SpellInfo.cpp69
-rw-r--r--src/server/game/Spells/SpellInfo.h4
-rw-r--r--src/server/game/Spells/SpellMgr.cpp6
-rw-r--r--src/server/game/Spells/SpellMgr.h3
-rw-r--r--src/server/game/World/World.cpp3
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)