aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Entities/AreaTrigger/AreaTrigger.cpp5
-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.cpp4
-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.h2
-rw-r--r--src/server/game/Entities/Item/Container/Bag.cpp2
-rw-r--r--src/server/game/Entities/Item/Item.cpp4
-rw-r--r--src/server/game/Entities/Object/BaseEntity.cpp647
-rw-r--r--src/server/game/Entities/Object/BaseEntity.h435
-rw-r--r--src/server/game/Entities/Object/Object.cpp660
-rw-r--r--src/server/game/Entities/Object/Object.h400
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h3
-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/Pet/Pet.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp8
-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.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp34
-rw-r--r--src/server/game/Entities/Unit/Unit.h8
-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/Spells/Auras/SpellAuraEffects.cpp6
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp4
-rw-r--r--src/server/game/Spells/Spell.cpp18
30 files changed, 1251 insertions, 1117 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp
index 234a03aa9d0..1a97401096a 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();
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..b25514ef657 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;
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..5b1ed3b0b0b 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);
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..a10d829c6b7 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);
diff --git a/src/server/game/Entities/Object/BaseEntity.cpp b/src/server/game/Entities/Object/BaseEntity.cpp
new file mode 100644
index 00000000000..3559c851d80
--- /dev/null
+++ b/src/server/game/Entities/Object/BaseEntity.cpp
@@ -0,0 +1,647 @@
+/*
+ * 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());
+ buf << uint8(1); // IndirectFragmentActive: CGObject
+ BuildValuesCreate(&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);
+
+ BuildValuesUpdate(&buf, fieldFlags, target);
+ buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4);
+
+ data->AddUpdateBlock();
+}
+
+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::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..b227a13f884
--- /dev/null
+++ b/src/server/game/Entities/Object/BaseEntity.h
@@ -0,0 +1,435 @@
+/*
+ * 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();
+
+ static ObjectGuid GetGUID(BaseEntity const* o) { return o ? o->GetGUID() : ObjectGuid::Empty; }
+ ObjectGuid const& GetGUID() const { return m_guid; }
+
+ 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 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;
+ 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);
+
+ TypeID m_objectTypeId = static_cast<TypeID>(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();
+ 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)
+{
+ BaseEntity* 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*/)
+{
+ BaseEntity* 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)
+{
+ BaseEntity* 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));
+
+ (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);
+ 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();
+}
+
+#endif // TRINITYCORE_BASE_ENTITY_H
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index ac2219ed5cf..9bf79150cb9 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>
@@ -73,46 +67,13 @@ Object::Object() : m_scriptRef(this, NoopObjectDeleter())
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();
- }
}
-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,12 +101,6 @@ 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;
@@ -256,505 +113,22 @@ void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* da
*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)
- {
- 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 && !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);
- 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;
-}
-
void Object::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag /*flags*/, Player const* /*target*/) const
{
*data << uint32(0);
}
-void Object::AddToObjectUpdateIfNeeded()
-{
- if (m_inWorld && !m_objectUpdated)
- m_objectUpdated = AddToObjectUpdate();
-}
-
void Object::ClearUpdateMask(bool remove)
{
m_values.ClearChangesMask(&Object::m_objectData);
- m_entityFragments.IdsChanged = false;
-
- if (m_objectUpdated)
- {
- if (remove)
- RemoveFromObjectUpdate();
- m_objectUpdated = false;
- }
-}
-
-void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const
-{
- UpdateDataMapType::iterator iter = data_map.try_emplace(player, player->GetMapId()).first;
- BuildValuesUpdateBlockForPlayer(&iter->second, iter->first);
+ BaseEntity::ClearUpdateMask(remove);
}
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 +188,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);
}
@@ -2591,10 +1967,10 @@ SpellMissInfo WorldObject::MagicSpellHitResult(Unit* victim, SpellInfo const* sp
// Parry
// For spells
// Resist
-SpellMissInfo WorldObject::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect /*= false*/) const
+SpellMissInfo WorldObject::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect, bool canImmune) const
{
// Check for immune
- if (victim->IsImmunedToSpell(spellInfo, this))
+ if (canImmune && victim->IsImmunedToSpell(spellInfo, MAX_EFFECT_MASK, this))
return SPELL_MISS_IMMUNE;
// Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
@@ -3747,6 +3123,12 @@ ObjectGuid WorldObject::GetTransGUID() const
return ObjectGuid::Empty;
}
+void WorldObject::SetTransport(TransportBase* t)
+{
+ m_transport = t;
+ m_updateFlag.MovementTransport = !m_updateFlag.MovementUpdate && t != nullptr;
+}
+
float WorldObject::GetFloorZ() const
{
if (!IsInWorld())
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 72b3a5dd448..f49614657f6 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();
- }
-
- virtual std::string GetDebugInfo() const;
+ std::string GetDebugInfo() const override;
Trinity::unique_weak_ptr<Object> GetWeakPtr() const { return m_scriptRef; }
@@ -330,242 +173,19 @@ 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 BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0;
+ void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0;
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;
struct NoopObjectDeleter { void operator()(Object*) const { /*noop - not managed*/ } };
Trinity::unique_trackable_ptr<Object> m_scriptRef;
-
- Object(Object const& right) = delete;
- Object(Object&& right) = delete;
- Object& operator=(Object const& right) = delete;
- Object& operator=(Object&& right) = delete;
};
-inline Object* UF::UpdateFieldHolder::GetOwner()
-{
-#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Winvalid-offsetof"
-#endif
-
- return reinterpret_cast<Object*>(reinterpret_cast<std::byte*>(this) - offsetof(Object, m_values));
-
-#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
-#pragma GCC diagnostic pop
-#endif
-}
-
-template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
-inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field)
-{
- Object* owner = GetOwner();
- owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
- if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
- _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
-
- return { (static_cast<Derived*>(owner)->*field)._value };
-}
-
-template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
-inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field)
-{
- Object* owner = GetOwner();
- owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
- if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
- _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
-
- return { static_cast<Derived*>(owner)->*field };
-}
-
-template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
-inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field, uint32 /*dummy*/)
-{
- Object* owner = GetOwner();
- owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
- if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
- _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
-
- auto& uf = (static_cast<Derived*>(owner)->*field);
- if (!uf.has_value())
- uf.ConstructValue();
-
- return { *uf._value };
-}
-
-template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
-inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit> Derived::* field)
-{
- Object* owner = GetOwner();
- owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
- if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
- {
- _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit);
- if (!_changesMask)
- owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
- }
- else
- owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
-
- (static_cast<Derived*>(owner)->*field)._value.ClearChangesMask();
-}
-
-template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
-inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field)
-{
- Object* owner = GetOwner();
- if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
- {
- _changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit);
- if (!_changesMask)
- owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
- }
- else
- owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
-
- auto& uf = (static_cast<Derived*>(owner)->*field);
- if (uf.has_value())
- uf._value->ClearChangesMask();
-}
-
template <class T_VALUES, class T_FLAGS, class FLAG_TYPE, size_t ARRAY_SIZE>
class FlaggedValuesArray32
{
@@ -837,7 +457,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
virtual float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const;
virtual SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const;
SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const;
- SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false) const;
+ SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect, bool canImmune) const;
void SendSpellMiss(Unit* target, uint32 spellID, SpellMissInfo missInfo);
virtual uint32 GetFaction() const = 0;
@@ -921,7 +541,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..0ba79357ecb 100644
--- a/src/server/game/Entities/Object/ObjectGuid.h
+++ b/src/server/game/Entities/Object/ObjectGuid.h
@@ -81,7 +81,7 @@ enum TypeMask
TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION
};
-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/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..68013f65654 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -387,7 +387,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;
@@ -3706,7 +3706,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 +17856,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
return false;
}
- Object::_Create(guid);
+ _Create(guid);
m_name = std::move(fields.name);
@@ -24097,7 +24097,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..3bca6f5d9cf 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)
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 6e888db12d1..f51da1de0f8 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -53,6 +53,7 @@
#include "Loot.h"
#include "LootMgr.h"
#include "LootPackets.h"
+#include "MapUtils.h"
#include "MiscPackets.h"
#include "MotionMaster.h"
#include "MovementGenerator.h"
@@ -312,7 +313,7 @@ Unit::Unit(bool isWorldObject) :
m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr),
i_motionMaster(std::make_unique<MotionMaster>(this)), m_regenTimer(0), m_vehicle(nullptr),
m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this),
- m_threatManager(this), m_aiLocked(false), _playHoverAnim(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0),
+ m_threatManager(this), m_aiLocked(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0),
_spellHistory(std::make_unique<SpellHistory>(this))
{
m_objectTypeId = TYPEID_UNIT;
@@ -1597,7 +1598,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
SpellInfo const* spellInfo = aurEff->GetSpellInfo();
// Damage shield can be resisted...
- SpellMissInfo missInfo = victim->SpellHitResult(this, spellInfo, false);
+ SpellMissInfo missInfo = victim->SpellHitResult(this, spellInfo, false, true);
if (missInfo != SPELL_MISS_NONE)
{
victim->SendSpellMiss(this, spellInfo->Id, missInfo);
@@ -5901,6 +5902,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
m_attacking = victim;
m_attacking->_addAttacker(this);
+ m_updateFlag.CombatVictim = true;
// Set our target
SetTarget(victim->GetGUID());
@@ -5951,6 +5953,7 @@ bool Unit::AttackStop()
Unit* victim = m_attacking;
+ m_updateFlag.CombatVictim = false;
m_attacking->_removeAttacker(this);
m_attacking = nullptr;
@@ -7743,7 +7746,7 @@ int32 Unit::SpellAbsorbBonusTaken(Unit* caster, SpellInfo const* spellProto, int
return static_cast<int32>(std::round(absorb));
}
-bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute /*= false*/) const
+bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute /*= false*/) const
{
if (!spellInfo)
return false;
@@ -7754,14 +7757,14 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste
if (!requireImmunityPurgesEffectAttribute)
return range.begin() != range.end();
- return std::any_of(range.begin(), range.end(), [](SpellImmuneContainer::value_type const& entry)
+ return std::ranges::any_of(range, [](uint32 immunitySpellId)
{
- if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(entry.second, DIFFICULTY_NONE))
+ if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(immunitySpellId, DIFFICULTY_NONE))
if (immunitySourceSpell->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
return true;
return false;
- });
+ }, Trinity::Containers::MapValue);
};
// Single spell immunity.
@@ -7792,7 +7795,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste
{
// State/effect immunities applied by aura expect full spell immunity
// Ignore effects with mechanic, they are supposed to be checked separately
- if (!spellEffectInfo.IsEffect())
+ if (!spellEffectInfo.IsEffect() || !(effectMask & (1 << spellEffectInfo.EffectIndex)))
continue;
if (!IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute))
{
@@ -7820,7 +7823,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste
if (!immuneSpellInfo || !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
continue;
- if (spellInfo->IsPositive() && !(immuneSpellInfo && immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)))
+ if (!(spellInfo->NegativeEffects.to_ulong() & effectMask) && !(immuneSpellInfo && immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS)))
continue;
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
@@ -7955,14 +7958,14 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo co
if (!requireImmunityPurgesEffectAttribute)
return range.begin() != range.end();
- return std::any_of(range.begin(), range.end(), [](SpellImmuneContainer::value_type const& entry)
+ return std::ranges::any_of(range, [](uint32 immunitySpellId)
{
- if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(entry.second, DIFFICULTY_NONE))
+ if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(immunitySpellId, DIFFICULTY_NONE))
if (immunitySourceSpell->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
return true;
return false;
- });
+ }, Trinity::Containers::MapValue);
};
// If m_immuneToEffect type contain this effect type, IMMUNE effect.
@@ -11200,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();
@@ -11216,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();
@@ -11232,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();
@@ -12283,7 +12289,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target)
if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_ALLOW_DEAD_TARGET))
return nullptr;
- if (target->IsImmunedToSpell(spellInfo, this))
+ if (target->IsImmunedToSpell(spellInfo, effMask, this))
return nullptr;
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
@@ -14073,7 +14079,7 @@ void Unit::SetPlayHoverAnim(bool enable, bool sendUpdate /*= true*/)
if (IsPlayingHoverAnim() == enable)
return;
- _playHoverAnim = enable;
+ m_updateFlag.PlayHoverAnim = enable;
if (!sendUpdate)
return;
@@ -14122,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 881aee786cb..ed291549b04 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1138,7 +1138,7 @@ class TC_GAME_API Unit : public WorldObject
void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
- bool IsPlayingHoverAnim() const { return _playHoverAnim; }
+ bool IsPlayingHoverAnim() const { return m_updateFlag.PlayHoverAnim; }
void SetPlayHoverAnim(bool enable, bool sendUpdate = true);
void CalculateHoverHeight();
void SetHoverHeight(float hoverHeight) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::HoverHeight), hoverHeight); }
@@ -1677,7 +1677,7 @@ class TC_GAME_API Unit : public WorldObject
static uint32 SpellCriticalHealingBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim);
void ApplySpellImmune(uint32 spellId, SpellImmunity op, uint32 type, bool apply);
- bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const;
+ bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const;
uint32 GetSchoolImmunityMask() const;
uint32 GetDamageImmunityMask() const;
uint64 GetMechanicImmunityMask() const;
@@ -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/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 25397d69d62..73fcbe1cefe 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -5629,7 +5629,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
// Consecrate ticks can miss and will not show up in the combat log
// dynobj auras must always have a caster
if (GetSpellEffectInfo().IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) &&
- ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE)
+ ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false, true) != SPELL_MISS_NONE)
return;
CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
@@ -5763,7 +5763,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
// dynobj auras must always have a caster
if (GetSpellEffectInfo().IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) &&
- ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE)
+ ASSERT_NOTNULL(caster)->SpellHitResult(target, GetSpellInfo(), false, true) != SPELL_MISS_NONE)
return;
CleanDamage cleanDamage = CleanDamage(0, 0, GetSpellInfo()->GetAttackType(), MELEE_HIT_NORMAL);
@@ -5953,7 +5953,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con
}
if (GetSpellEffectInfo().IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA) &&
- caster->SpellHitResult(target, GetSpellInfo(), false) != SPELL_MISS_NONE)
+ caster->SpellHitResult(target, GetSpellInfo(), false, true) != SPELL_MISS_NONE)
return;
// ignore negative values (can be result apply spellmods to aura damage
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 7a4722f9cef..3a68549f238 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -684,7 +684,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
{
// needs readding - remove now, will be applied in next update cycle
// (dbcs do not have auras which apply on same type of targets but have different radius, so this is not really needed)
- if (itr->first->IsImmunedToSpell(GetSpellInfo(), caster, true) || !CanBeAppliedOn(itr->first))
+ if (itr->first->IsImmunedToSpell(GetSpellInfo(), itr->second, caster, true) || !CanBeAppliedOn(itr->first))
{
targetsToRemove.push_back(applicationPair.second->GetTarget());
continue;
@@ -717,7 +717,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), spellEffectInfo, caster))
itr->second &= ~(1 << spellEffectInfo.EffectIndex);
- if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), caster) || !CanBeAppliedOn(itr->first))
+ if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), itr->second, caster) || !CanBeAppliedOn(itr->first))
addUnit = false;
}
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 50db01b3fec..8cc94e2f59d 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2434,7 +2434,9 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
// Calculate hit result
WorldObject* caster = m_originalCaster ? m_originalCaster : m_caster;
- targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo, m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target)));
+ targetInfo.MissCondition = caster->SpellHitResult(target, m_spellInfo,
+ m_canReflect && !(IsPositive() && m_caster->IsFriendlyTo(target)),
+ false /*immunity will be checked after complete EffectMask is known*/);
// Spell have speed - need calculate incoming time
// Incoming time is zero for self casts. At least I think so.
@@ -2479,7 +2481,9 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
{
// Calculate reflected spell result on caster (shouldn't be able to reflect gameobject spells)
Unit* unitCaster = ASSERT_NOTNULL(m_caster->ToUnit());
- targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo, false); // can't reflect twice
+ targetInfo.ReflectResult = unitCaster->SpellHitResult(unitCaster, m_spellInfo,
+ false /*can't reflect twice*/,
+ false /*immunity will be checked after complete EffectMask is known*/);
// Proc spell reflect aura when missile hits the original target
target->m_Events.AddEvent(new ProcReflectDelayed(target, m_originalCasterGUID), target->m_Events.CalculateTime(Milliseconds(targetInfo.TimeDelay)));
@@ -3099,7 +3103,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, TargetInfo& hitInfo)
return SPELL_MISS_EVADE;
// For delayed spells immunity may be applied between missile launch and hit - check immunity for that case
- if (hitInfo.TimeDelay && unit->IsImmunedToSpell(m_spellInfo, m_caster))
+ if (hitInfo.TimeDelay && unit->IsImmunedToSpell(m_spellInfo, hitInfo.EffectMask, m_caster))
return SPELL_MISS_IMMUNE;
CallScriptBeforeHitHandlers(hitInfo.MissCondition);
@@ -8612,6 +8616,10 @@ void Spell::PreprocessSpellLaunch(TargetInfo& targetInfo)
if (!targetUnit)
return;
+ // Check immunity now that EffectMask is known
+ if (targetUnit->IsImmunedToSpell(GetSpellInfo(), targetInfo.EffectMask, m_caster))
+ targetInfo.MissCondition = SPELL_MISS_IMMUNE;
+
// This will only cause combat - the target will engage once the projectile hits (in Spell::TargetInfo::PreprocessTarget)
if (m_originalCaster && targetInfo.MissCondition != SPELL_MISS_EVADE && !m_originalCaster->IsFriendlyTo(targetUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || targetUnit->IsEngaged()))
m_originalCaster->SetInCombatWith(targetUnit, true);
@@ -8622,7 +8630,11 @@ void Spell::PreprocessSpellLaunch(TargetInfo& targetInfo)
unit = targetUnit;
// In case spell reflect from target, do all effect on caster (if hit)
else if (targetInfo.MissCondition == SPELL_MISS_REFLECT && targetInfo.ReflectResult == SPELL_MISS_NONE)
+ {
unit = m_caster->ToUnit();
+ if (unit && unit->IsImmunedToSpell(GetSpellInfo(), targetInfo.EffectMask, unit))
+ targetInfo.ReflectResult = SPELL_MISS_IMMUNE;
+ }
if (!unit)
return;