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