Core/Objects: Create a base class for Object to support creating new client visible object types

This commit is contained in:
Shauren
2025-12-27 11:43:04 +01:00
parent 3b722eb93e
commit 0ff5cf4d00
18 changed files with 1121 additions and 1031 deletions
@@ -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);
@@ -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();
+3 -3
View File
@@ -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());
@@ -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;
@@ -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();
@@ -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);
@@ -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);
+2 -2
View File
@@ -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);
@@ -0,0 +1,647 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BaseEntity.h"
#include "Conversation.h"
#include "DB2Structure.h"
#include "Errors.h"
#include "GameTime.h"
#include "Log.h"
#include "MovementPackets.h"
#include "Player.h"
#include "SmoothPhasing.h"
#include "Transport.h"
#include "UpdateData.h"
#include "Vehicle.h"
BaseEntity::BaseEntity() = default;
BaseEntity::~BaseEntity()
{
if (IsInWorld())
{
TC_LOG_FATAL("misc", "BaseEntity::~BaseEntity {} deleted but still in world!!", GetGUID());
ABORT();
}
if (m_objectUpdated)
{
TC_LOG_FATAL("misc", "BaseEntity::~BaseEntity {} deleted but still in update list!!", GetGUID());
ABORT();
}
}
void BaseEntity::AddToWorld()
{
if (m_inWorld)
return;
m_inWorld = true;
// synchronize values mirror with values array (changes will send in updatecreate opcode any way
ASSERT(!m_objectUpdated);
ClearUpdateMask(false);
}
void BaseEntity::RemoveFromWorld()
{
if (!m_inWorld)
return;
m_inWorld = false;
// if we remove from world then sending changes not required
ClearUpdateMask(true);
}
void BaseEntity::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const
{
if (!target)
return;
uint8 updateType = m_isNewObject ? UPDATETYPE_CREATE_OBJECT2 : UPDATETYPE_CREATE_OBJECT;
uint8 objectType = m_objectTypeId;
CreateObjectBits flags = m_updateFlag;
if (target == this) // building packet for yourself
{
flags.ThisIsYou = true;
flags.ActivePlayer = true;
objectType = TYPEID_ACTIVE_PLAYER;
}
if (IsWorldObject())
{
WorldObject const* worldObject = static_cast<WorldObject const*>(this);
if (worldObject->GetSmoothPhasing() && worldObject->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID()))
flags.SmoothPhasing = true;
}
ByteBuffer& buf = data->GetBuffer();
buf << uint8(updateType);
buf << GetGUID();
buf << uint8(objectType);
BuildMovementUpdate(&buf, flags, target);
UF::UpdateFieldFlag fieldFlags = GetUpdateFieldFlagsFor(target);
std::size_t sizePos = buf.wpos();
buf << uint32(0);
buf << uint8(fieldFlags);
BuildEntityFragments(&buf, m_entityFragments.GetIds());
buf << uint8(1); // IndirectFragmentActive: CGObject
BuildValuesCreate(&buf, fieldFlags, target);
buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4);
data->AddUpdateBlock();
}
void BaseEntity::SendUpdateToPlayer(Player* player) const
{
// send create update to player
UpdateData upd(player->GetMapId());
WorldPacket packet;
if (player->HaveAtClient(this))
BuildValuesUpdateBlockForPlayer(&upd, player);
else
BuildCreateUpdateBlockForPlayer(&upd, player);
upd.BuildPacket(&packet);
player->SendDirectMessage(&packet);
}
void BaseEntity::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const
{
ByteBuffer& buf = PrepareValuesUpdateBuffer(data);
EnumFlag<UF::UpdateFieldFlag> fieldFlags = GetUpdateFieldFlagsFor(target);
std::size_t sizePos = buf.wpos();
buf << uint32(0);
buf << uint8(fieldFlags.HasFlag(UF::UpdateFieldFlag::Owner));
buf << uint8(m_entityFragments.IdsChanged);
if (m_entityFragments.IdsChanged)
{
buf << uint8(WowCS::EntityFragmentSerializationType::Full);
BuildEntityFragments(&buf, m_entityFragments.GetIds());
}
buf << uint8(m_entityFragments.ContentsChangedMask);
BuildValuesUpdate(&buf, fieldFlags, target);
buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4);
data->AddUpdateBlock();
}
void BaseEntity::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments)
{
data->append(fragments.data(), fragments.size());
*data << uint8(WowCS::EntityFragment::End);
}
void BaseEntity::BuildDestroyUpdateBlock(UpdateData* data) const
{
data->AddDestroyObject(GetGUID());
}
void BaseEntity::BuildOutOfRangeUpdateBlock(UpdateData* data) const
{
data->AddOutOfRangeGUID(GetGUID());
}
ByteBuffer& BaseEntity::PrepareValuesUpdateBuffer(UpdateData* data) const
{
ByteBuffer& buffer = data->GetBuffer();
buffer << uint8(UPDATETYPE_VALUES);
buffer << GetGUID();
return buffer;
}
void BaseEntity::DestroyForPlayer(Player const* target) const
{
ASSERT(target);
UpdateData updateData(target->GetMapId());
BuildDestroyUpdateBlock(&updateData);
WorldPacket packet;
updateData.BuildPacket(&packet);
target->SendDirectMessage(&packet);
}
void BaseEntity::SendOutOfRangeForPlayer(Player const* target) const
{
ASSERT(target);
UpdateData updateData(target->GetMapId());
BuildOutOfRangeUpdateBlock(&updateData);
WorldPacket packet;
updateData.BuildPacket(&packet);
target->SendDirectMessage(&packet);
}
void BaseEntity::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const
{
std::span<uint32 const> PauseTimes;
if (IsGameObject())
PauseTimes = static_cast<GameObject const*>(this)->GetPauseTimes();
data->WriteBit(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.size());
if (flags.Stationary)
{
WorldObject const* self = static_cast<WorldObject const*>(this);
*data << self->GetStationaryPosition().PositionXYZOStream();
}
if (flags.CombatVictim)
{
Unit const* unit = static_cast<Unit const*>(this);
*data << unit->GetVictim()->GetGUID(); // CombatVictim
}
if (flags.ServerTime)
*data << uint32(GameTime::GetGameTimeMS());
if (flags.Vehicle)
{
Unit const* unit = static_cast<Unit const*>(this);
*data << uint32(unit->GetVehicleKit()->GetVehicleInfo()->ID); // RecID
*data << float(unit->GetOrientation()); // InitialRawFacing
}
if (flags.AnimKit)
{
WorldObject const* self = static_cast<WorldObject const*>(this);
*data << uint16(self->GetAIAnimKitId()); // AiID
*data << uint16(self->GetMovementAnimKitId()); // MovementID
*data << uint16(self->GetMeleeAnimKitId()); // MeleeID
}
if (flags.Rotation)
{
GameObject const* gameObject = static_cast<GameObject const*>(this);
*data << uint64(gameObject->GetPackedLocalRotation()); // Rotation
}
//if (flags.Room)
// *data << ObjectGuid(HouseGUID);
//if (flags.Decor)
// *data << ObjectGuid(RoomGUID);
//if (flags.MeshObject)
//{
// *data << ObjectGuid(AttachParentGUID);
// *data << TaggedPosition<Position::XYZ>(PositionLocalSpace);
// *data << QuaternionData(RotationLocalSpace);
// *data << float(ScaleLocalSpace);
// *data << uint8(AttachmentFlags);
//}
if (!PauseTimes.empty())
data->append(PauseTimes.data(), PauseTimes.size());
if (flags.MovementTransport)
{
WorldObject const* self = static_cast<WorldObject const*>(this);
*data << self->m_movementInfo.transport;
}
if (flags.GameObject)
{
GameObject const* gameObject = static_cast<GameObject const*>(this);
bool bit8 = false;
bool isTransport = gameObject->GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT;
*data << uint32(gameObject->GetWorldEffectID());
data->WriteBit(bit8);
data->WriteBit(isTransport);
data->WriteBit(gameObject->GetPathProgressForClient().has_value());
data->FlushBits();
if (isTransport)
{
Transport const* transport = static_cast<Transport const*>(gameObject);
uint32 period = transport->GetTransportPeriod();
*data << uint32((((int64(transport->GetTimer()) - int64(GameTime::GetGameTimeMS())) % period) + period) % period); // TimeOffset
*data << uint32(transport->GetNextStopTimestamp().value_or(0));
data->WriteBit(transport->GetNextStopTimestamp().has_value());
data->WriteBit(transport->IsStopped());
data->WriteBit(false);
data->FlushBits();
}
if (bit8)
*data << uint32(0);
if (gameObject->GetPathProgressForClient())
*data << float(*gameObject->GetPathProgressForClient());
}
if (flags.SmoothPhasing)
{
SmoothPhasingInfo const* smoothPhasingInfo = static_cast<WorldObject const*>(this)->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID());
ASSERT(smoothPhasingInfo);
data->WriteBit(smoothPhasingInfo->ReplaceActive);
data->WriteBit(smoothPhasingInfo->StopAnimKits);
data->WriteBit(smoothPhasingInfo->ReplaceObject.has_value());
data->FlushBits();
if (smoothPhasingInfo->ReplaceObject)
*data << *smoothPhasingInfo->ReplaceObject;
}
if (flags.SceneObject)
{
data->WriteBit(false); // HasLocalScriptData
data->WriteBit(false); // HasPetBattleFullUpdate
data->FlushBits();
// if (HasLocalScriptData)
// {
// data->WriteBits(Data.length(), 7);
// data->FlushBits();
// data->WriteString(Data);
// }
// if (HasPetBattleFullUpdate)
// {
// for (std::size_t i = 0; i < 2; ++i)
// {
// *data << ObjectGuid(Players[i].CharacterID);
// *data << int32(Players[i].TrapAbilityID);
// *data << int32(Players[i].TrapStatus);
// *data << uint16(Players[i].RoundTimeSecs);
// *data << int8(Players[i].FrontPet);
// *data << uint8(Players[i].InputFlags);
// data->WriteBits(Players[i].Pets.size(), 2);
// data->FlushBits();
// for (std::size_t j = 0; j < Players[i].Pets.size(); ++j)
// {
// *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID);
// *data << int32(Players[i].Pets[j].SpeciesID);
// *data << int32(Players[i].Pets[j].CreatureID);
// *data << int32(Players[i].Pets[j].DisplayID);
// *data << int16(Players[i].Pets[j].Level);
// *data << int16(Players[i].Pets[j].Xp);
// *data << int32(Players[i].Pets[j].CurHealth);
// *data << int32(Players[i].Pets[j].MaxHealth);
// *data << int32(Players[i].Pets[j].Power);
// *data << int32(Players[i].Pets[j].Speed);
// *data << int32(Players[i].Pets[j].NpcTeamMemberID);
// *data << uint8(Players[i].Pets[j].BreedQuality);
// *data << uint16(Players[i].Pets[j].StatusFlags);
// *data << int8(Players[i].Pets[j].Slot);
// *data << uint32(Players[i].Pets[j].Abilities.size());
// *data << uint32(Players[i].Pets[j].Auras.size());
// *data << uint32(Players[i].Pets[j].States.size());
// for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k)
// {
// *data << int32(Players[i].Pets[j].Abilities[k].AbilityID);
// *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining);
// *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining);
// *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex);
// *data << uint8(Players[i].Pets[j].Abilities[k].Pboid);
// }
// for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k)
// {
// *data << int32(Players[i].Pets[j].Auras[k].AbilityID);
// *data << uint32(Players[i].Pets[j].Auras[k].InstanceID);
// *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining);
// *data << int32(Players[i].Pets[j].Auras[k].CurrentRound);
// *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID);
// }
// for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k)
// {
// *data << uint32(Players[i].Pets[j].States[k].StateID);
// *data << int32(Players[i].Pets[j].States[k].StateValue);
// }
// data->WriteBits(Players[i].Pets[j].CustomName.length(), 7);
// data->FlushBits();
// data->WriteString(Players[i].Pets[j].CustomName);
// }
// }
// for (std::size_t i = 0; i < 3; ++i)
// {
// *data << uint32(Enviros[j].Auras.size());
// *data << uint32(Enviros[j].States.size());
// for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j)
// {
// *data << int32(Enviros[j].Auras[j].AbilityID);
// *data << uint32(Enviros[j].Auras[j].InstanceID);
// *data << int32(Enviros[j].Auras[j].RoundsRemaining);
// *data << int32(Enviros[j].Auras[j].CurrentRound);
// *data << uint8(Enviros[j].Auras[j].CasterPBOID);
// }
// for (std::size_t j = 0; j < Enviros[j].States.size(); ++j)
// {
// *data << uint32(Enviros[i].States[j].StateID);
// *data << int32(Enviros[i].States[j].StateValue);
// }
// }
// *data << uint16(WaitingForFrontPetsMaxSecs);
// *data << uint16(PvpMaxRoundTime);
// *data << int32(CurRound);
// *data << uint32(NpcCreatureID);
// *data << uint32(NpcDisplayID);
// *data << int8(CurPetBattleState);
// *data << uint8(ForfeitPenalty);
// *data << ObjectGuid(InitialWildPetGUID);
// data->WriteBit(IsPVP);
// data->WriteBit(CanAwardXP);
// data->FlushBits();
// }
}
if (flags.ActivePlayer)
{
Player const* player = static_cast<Player const*>(this);
bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty();
bool HasRuneState = player->GetPowerIndex(POWER_RUNES) != MAX_POWERS;
data->WriteBit(HasSceneInstanceIDs);
data->WriteBit(HasRuneState);
data->FlushBits();
if (HasSceneInstanceIDs)
{
*data << uint32(player->GetSceneMgr().GetSceneTemplateByInstanceMap().size());
for (auto const& [sceneInstanceId, _] : player->GetSceneMgr().GetSceneTemplateByInstanceMap())
*data << uint32(sceneInstanceId);
}
if (HasRuneState)
{
float baseCd = float(player->GetRuneBaseCooldown());
uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES));
*data << uint8((1 << maxRunes) - 1);
*data << uint8(player->GetRunesState());
*data << uint32(maxRunes);
for (uint32 i = 0; i < maxRunes; ++i)
*data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255);
}
}
if (flags.Conversation)
{
Conversation const* self = static_cast<Conversation const*>(this);
if (data->WriteBit(self->GetTextureKitId() != 0))
*data << uint32(self->GetTextureKitId());
data->FlushBits();
}
}
UF::UpdateFieldFlag BaseEntity::GetUpdateFieldFlagsFor(Player const* /*target*/) const
{
return UF::UpdateFieldFlag::None;
}
void BaseEntity::AddToObjectUpdateIfNeeded()
{
if (m_inWorld && !m_objectUpdated)
m_objectUpdated = AddToObjectUpdate();
}
void BaseEntity::ClearUpdateMask(bool remove)
{
m_entityFragments.IdsChanged = false;
if (m_objectUpdated)
{
if (remove)
RemoveFromObjectUpdate();
m_objectUpdated = false;
}
}
void BaseEntity::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const
{
UpdateDataMapType::iterator iter = data_map.try_emplace(player, player->GetMapId()).first;
BuildValuesUpdateBlockForPlayer(&iter->second, iter->first);
}
std::string BaseEntity::GetDebugInfo() const
{
return Trinity::StringFormat("{}", GetGUID());
}
@@ -0,0 +1,434 @@
/*
* 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 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 BaseEntity* GetOwner();
private:
friend BaseEntity;
// This class is tightly tied to BaseEntity::m_values member, do not construct elsewhere
UpdateFieldHolder() = default;
uint32 _changesMask = 0; // changes mask for data of Object subclasses
};
template<typename T>
inline bool SetUpdateFieldValue(UpdateFieldPrivateSetter<T>& setter, typename UpdateFieldPrivateSetter<T>::value_type&& value)
{
return setter.SetValue(std::move(value));
}
template<typename T>
inline typename DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter)
{
return setter.AddValue();
}
template<typename T>
inline typename DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index)
{
return setter.InsertValue(index);
}
template<typename T>
inline void RemoveDynamicUpdateFieldValue(DynamicUpdateFieldSetter<T>& setter, uint32 index)
{
setter.RemoveValue(index);
}
template<typename T>
inline void ClearDynamicUpdateFieldValues(DynamicUpdateFieldSetter<T>& setter)
{
setter.Clear();
}
template<typename K, typename V>
inline void RemoveMapUpdateFieldValue(MapUpdateFieldSetter<K, V>& setter, std::type_identity_t<K> const& key)
{
setter.RemoveKey(key);
}
template<typename T>
inline void RemoveOptionalUpdateFieldValue(OptionalUpdateFieldSetter<T>& setter)
{
setter.RemoveValue();
}
}
class TC_GAME_API BaseEntity
{
ObjectGuid m_guid;
public:
virtual ~BaseEntity();
bool IsInWorld() const { return m_inWorld; }
virtual void AddToWorld();
virtual void RemoveFromWorld();
static ObjectGuid GetGUID(BaseEntity const* o) { return o ? o->GetGUID() : ObjectGuid::Empty; }
ObjectGuid const& GetGUID() const { return m_guid; }
TypeID GetTypeId() const { return m_objectTypeId; }
bool isType(TypeMask mask) const { return (ObjectTypeMask[m_objectTypeId] & mask) != 0; }
inline bool IsWorldObject() const { return isType(TYPEMASK_WORLDOBJECT); }
inline bool IsItem() const { return isType(TYPEMASK_ITEM); }
inline bool IsUnit() const { return isType(TYPEMASK_UNIT); }
inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; }
inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; }
inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; }
inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; }
inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; }
inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; }
inline bool IsSceneObject() const { return GetTypeId() == TYPEID_SCENEOBJECT; }
inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; }
inline bool IsMeshObject() const { return GetTypeId() == TYPEID_MESH_OBJECT; }
virtual void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const;
void SendUpdateToPlayer(Player* player) const;
void BuildValuesUpdateBlockForPlayer(UpdateData* data, Player const* target) const;
void BuildDestroyUpdateBlock(UpdateData* data) const;
void BuildOutOfRangeUpdateBlock(UpdateData* data) const;
ByteBuffer& PrepareValuesUpdateBuffer(UpdateData* data) const;
virtual void DestroyForPlayer(Player const* target) const;
void SendOutOfRangeForPlayer(Player const* target) const;
virtual void ClearUpdateMask(bool remove);
virtual std::string GetNameForLocaleIdx(LocaleConstant locale) const = 0;
void SetIsNewObject(bool enable) { m_isNewObject = enable; }
bool IsDestroyedObject() const { return m_isDestroyedObject; }
void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; }
virtual void BuildUpdate([[maybe_unused]] UpdateDataMapType& data_map) { }
void BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const;
friend UF::UpdateFieldHolder;
UF::UpdateFieldHolder m_values;
template<typename T>
void ForceUpdateFieldChange(UF::UpdateFieldPrivateSetter<T> const& /*setter*/)
{
AddToObjectUpdateIfNeeded();
}
virtual std::string GetDebugInfo() const;
protected:
BaseEntity();
void _Create(ObjectGuid const& guid) { m_guid = guid; }
template<typename T>
void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value)
{
if (UF::SetUpdateFieldValue(setter, std::move(value)))
AddToObjectUpdateIfNeeded();
}
template<typename T>
void SetUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag)
{
static_assert(std::is_integral_v<T>, "SetUpdateFieldFlagValue must be used with integral types");
this->SetUpdateFieldValue(setter, setter.GetValue() | flag);
}
template<typename T>
void RemoveUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag)
{
static_assert(std::is_integral_v<T>, "RemoveUpdateFieldFlagValue must be used with integral types");
this->SetUpdateFieldValue(setter, setter.GetValue() & ~flag);
}
template<typename T>
typename UF::DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter)
{
AddToObjectUpdateIfNeeded();
return UF::AddDynamicUpdateFieldValue(setter);
}
template<typename T>
typename UF::DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index)
{
AddToObjectUpdateIfNeeded();
return UF::InsertDynamicUpdateFieldValue(setter, index);
}
template<typename T>
void RemoveDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index)
{
AddToObjectUpdateIfNeeded();
UF::RemoveDynamicUpdateFieldValue(setter, index);
}
template<typename K, typename V>
void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter<K, V> setter, std::type_identity_t<K> const& key)
{
AddToObjectUpdateIfNeeded();
UF::RemoveMapUpdateFieldValue(setter, key);
}
template<typename T>
void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter<T> setter)
{
AddToObjectUpdateIfNeeded();
UF::ClearDynamicUpdateFieldValues(setter);
}
template<typename T>
void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter<T> setter)
{
AddToObjectUpdateIfNeeded();
UF::RemoveOptionalUpdateFieldValue(setter);
}
// stat system helpers
template<typename T>
void SetUpdateFieldStatValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value)
{
static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types");
this->SetUpdateFieldValue(setter, std::max(value, T(0)));
}
template<typename T>
void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type mod, bool apply)
{
static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types");
T value = setter.GetValue();
if (apply)
value += mod;
else
value -= mod;
this->SetUpdateFieldValue(setter, value);
}
template<typename T>
void ApplyPercentModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, float percent, bool apply)
{
static_assert(std::is_arithmetic_v<T>, "SetUpdateFieldStatValue must be used with arithmetic types");
T value = setter.GetValue();
// don't want to include Util.h here
//ApplyPercentModFloatVar(value, percent, apply);
if (percent == -100.0f)
percent = -99.99f;
value *= (apply ? (100.0f + percent) / 100.0f : 100.0f / (100.0f + percent));
this->SetUpdateFieldValue(setter, value);
}
template<typename Action>
void DoWithSuppressingObjectUpdates(Action&& action)
{
bool wasUpdatedBeforeAction = m_objectUpdated;
action();
if (m_objectUpdated && !wasUpdatedBeforeAction)
{
RemoveFromObjectUpdate();
m_objectUpdated = false;
}
}
void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const;
virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const;
virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0;
virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0;
static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments);
TypeID m_objectTypeId = static_cast<TypeID>(NUM_CLIENT_OBJECT_TYPES);
CreateObjectBits m_updateFlag = {};
WowCS::EntityFragmentsHolder m_entityFragments;
virtual bool AddToObjectUpdate() = 0;
virtual void RemoveFromObjectUpdate() = 0;
void AddToObjectUpdateIfNeeded();
bool m_objectUpdated = false;
private:
bool m_inWorld = false;
bool m_isNewObject = false;
bool m_isDestroyedObject = false;
BaseEntity(BaseEntity const& right) = delete;
BaseEntity(BaseEntity&& right) = delete;
BaseEntity& operator=(BaseEntity const& right) = delete;
BaseEntity& operator=(BaseEntity&& right) = delete;
};
inline BaseEntity* UF::UpdateFieldHolder::GetOwner()
{
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
return reinterpret_cast<BaseEntity*>(reinterpret_cast<std::byte*>(this) - offsetof(BaseEntity, m_values));
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
#pragma GCC diagnostic pop
#endif
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field)
{
BaseEntity* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
_changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
return { (static_cast<Derived*>(owner)->*field)._value };
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field)
{
BaseEntity* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
_changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
return { static_cast<Derived*>(owner)->*field };
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field, uint32 /*dummy*/)
{
BaseEntity* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
_changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
auto& uf = (static_cast<Derived*>(owner)->*field);
if (!uf.has_value())
uf.ConstructValue();
return { *uf._value };
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit> Derived::* field)
{
BaseEntity* owner = GetOwner();
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
{
_changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit);
if (!_changesMask)
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
}
else
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
(static_cast<Derived*>(owner)->*field)._value.ClearChangesMask();
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field)
{
BaseEntity* owner = GetOwner();
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
{
_changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit);
if (!_changesMask)
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
}
else
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
auto& uf = (static_cast<Derived*>(owner)->*field);
if (uf.has_value())
uf._value->ClearChangesMask();
}
#endif // TRINITYCORE_BASE_ENTITY_H
+11 -623
View File
@@ -20,19 +20,16 @@
#include "CellImpl.h"
#include "CinematicMgr.h"
#include "CombatLogPackets.h"
#include "Common.h"
#include "Creature.h"
#include "Corpse.h"
#include "CreatureGroups.h"
#include "DB2Stores.h"
#include "GameTime.h"
#include "DynamicObject.h"
#include "GameObject.h"
#include "GridNotifiersImpl.h"
#include "G3DPosition.hpp"
#include "InstanceScenario.h"
#include "Item.h"
#include "Log.h"
#include "Map.h"
#include "MiscPackets.h"
#include "MovementPackets.h"
#include "MovementTypedefs.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
@@ -41,17 +38,14 @@
#include "Player.h"
#include "ReputationMgr.h"
#include "SmoothPhasing.h"
#include "Spell.h"
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "SpellPackets.h"
#include "TemporarySummon.h"
#include "Totem.h"
#include "Transport.h"
#include "Unit.h"
#include "UpdateData.h"
#include "Util.h"
#include "VMapFactory.h"
#include "Vehicle.h"
#include "VMapManager.h"
#include "World.h"
#include <G3D/Vector3.h>
@@ -73,46 +67,13 @@ Object::Object() : m_scriptRef(this, NoopObjectDeleter())
m_updateFlag.Clear();
m_entityFragments.Add(WowCS::EntityFragment::CGObject, false);
m_inWorld = false;
m_isNewObject = false;
m_isDestroyedObject = false;
m_objectUpdated = false;
}
Object::~Object()
{
if (IsInWorld())
{
TC_LOG_FATAL("misc", "Object::~Object {} deleted but still in world!!", GetGUID().ToString());
if (Item* item = ToItem())
TC_LOG_FATAL("misc", "Item slot {}", item->GetSlot());
ABORT();
}
if (m_objectUpdated)
{
TC_LOG_FATAL("misc", "Object::~Object {} deleted but still in update list!!", GetGUID().ToString());
ABORT();
}
}
void Object::_Create(ObjectGuid const& guid)
{
m_objectUpdated = false;
m_guid = guid;
}
Object::~Object() = default;
void Object::AddToWorld()
{
if (m_inWorld)
return;
m_inWorld = true;
// synchronize values mirror with values array (changes will send in updatecreate opcode any way
ASSERT(!m_objectUpdated);
ClearUpdateMask(false);
BaseEntity::AddToWorld();
// Set new ref when adding to world (except if we already have one - also set in constructor to allow scripts to work in initialization phase)
// Changing the ref when adding/removing from world prevents accessing players on different maps (possibly from another thread)
@@ -122,95 +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 (worldObject->GetSmoothPhasing() && worldObject->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID()))
flags.SmoothPhasing = true;
}
ByteBuffer& buf = data->GetBuffer();
buf << uint8(updateType);
buf << GetGUID();
buf << uint8(objectType);
BuildMovementUpdate(&buf, flags, target);
UF::UpdateFieldFlag fieldFlags = GetUpdateFieldFlagsFor(target);
std::size_t sizePos = buf.wpos();
buf << uint32(0);
buf << uint8(fieldFlags);
BuildEntityFragments(&buf, m_entityFragments.GetIds());
buf << uint8(1); // IndirectFragmentActive: CGObject
BuildValuesCreate(&buf, fieldFlags, target);
buf.put<uint32>(sizePos, buf.wpos() - sizePos - 4);
data->AddUpdateBlock();
}
void 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);
@@ -224,12 +101,6 @@ void Object::BuildValuesUpdateBlockForPlayerWithFlag(UpdateData* data, UF::Updat
data->AddUpdateBlock();
}
void Object::BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments)
{
data->append(fragments.data(), fragments.size());
*data << uint8(WowCS::EntityFragment::End);
}
void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const
{
uint8 contentsChangedMask = WowCS::CGObjectChangedMask;
@@ -242,505 +113,22 @@ void Object::BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* da
*data << uint8(contentsChangedMask);
}
void Object::BuildDestroyUpdateBlock(UpdateData* data) const
{
data->AddDestroyObject(GetGUID());
}
void Object::BuildOutOfRangeUpdateBlock(UpdateData* data) const
{
data->AddOutOfRangeGUID(GetGUID());
}
ByteBuffer& Object::PrepareValuesUpdateBuffer(UpdateData* data) const
{
ByteBuffer& buffer = data->GetBuffer();
buffer << uint8(UPDATETYPE_VALUES);
buffer << GetGUID();
return buffer;
}
void Object::DestroyForPlayer(Player const* target) const
{
ASSERT(target);
UpdateData updateData(target->GetMapId());
BuildDestroyUpdateBlock(&updateData);
WorldPacket packet;
updateData.BuildPacket(&packet);
target->SendDirectMessage(&packet);
}
void Object::SendOutOfRangeForPlayer(Player const* 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::span<uint32 const> PauseTimes;
if (IsGameObject())
PauseTimes = static_cast<GameObject const*>(this)->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.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);
Transport const* transport = gameObject->ToTransport();
bool bit8 = false;
*data << uint32(gameObject->GetWorldEffectID());
data->WriteBit(bit8);
data->WriteBit(transport != nullptr);
data->WriteBit(gameObject->GetPathProgressForClient().has_value());
data->FlushBits();
if (transport)
{
uint32 period = transport->GetTransportPeriod();
*data << uint32((((int64(transport->GetTimer()) - int64(GameTime::GetGameTimeMS())) % period) + period) % period); // TimeOffset
*data << uint32(transport->GetNextStopTimestamp().value_or(0));
data->WriteBit(transport->GetNextStopTimestamp().has_value());
data->WriteBit(transport->IsStopped());
data->WriteBit(false);
data->FlushBits();
}
if (bit8)
*data << uint32(0);
if (gameObject->GetPathProgressForClient())
*data << float(*gameObject->GetPathProgressForClient());
}
if (flags.SmoothPhasing)
{
SmoothPhasingInfo const* smoothPhasingInfo = static_cast<WorldObject const*>(this)->GetSmoothPhasing()->GetInfoForSeer(target->GetGUID());
ASSERT(smoothPhasingInfo);
data->WriteBit(smoothPhasingInfo->ReplaceActive);
data->WriteBit(smoothPhasingInfo->StopAnimKits);
data->WriteBit(smoothPhasingInfo->ReplaceObject.has_value());
data->FlushBits();
if (smoothPhasingInfo->ReplaceObject)
*data << *smoothPhasingInfo->ReplaceObject;
}
if (flags.SceneObject)
{
data->WriteBit(false); // HasLocalScriptData
data->WriteBit(false); // HasPetBattleFullUpdate
data->FlushBits();
// if (HasLocalScriptData)
// {
// data->WriteBits(Data.length(), 7);
// data->FlushBits();
// data->WriteString(Data);
// }
// if (HasPetBattleFullUpdate)
// {
// for (std::size_t i = 0; i < 2; ++i)
// {
// *data << ObjectGuid(Players[i].CharacterID);
// *data << int32(Players[i].TrapAbilityID);
// *data << int32(Players[i].TrapStatus);
// *data << uint16(Players[i].RoundTimeSecs);
// *data << int8(Players[i].FrontPet);
// *data << uint8(Players[i].InputFlags);
// data->WriteBits(Players[i].Pets.size(), 2);
// data->FlushBits();
// for (std::size_t j = 0; j < Players[i].Pets.size(); ++j)
// {
// *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID);
// *data << int32(Players[i].Pets[j].SpeciesID);
// *data << int32(Players[i].Pets[j].CreatureID);
// *data << int32(Players[i].Pets[j].DisplayID);
// *data << int16(Players[i].Pets[j].Level);
// *data << int16(Players[i].Pets[j].Xp);
// *data << int32(Players[i].Pets[j].CurHealth);
// *data << int32(Players[i].Pets[j].MaxHealth);
// *data << int32(Players[i].Pets[j].Power);
// *data << int32(Players[i].Pets[j].Speed);
// *data << int32(Players[i].Pets[j].NpcTeamMemberID);
// *data << uint8(Players[i].Pets[j].BreedQuality);
// *data << uint16(Players[i].Pets[j].StatusFlags);
// *data << int8(Players[i].Pets[j].Slot);
// *data << uint32(Players[i].Pets[j].Abilities.size());
// *data << uint32(Players[i].Pets[j].Auras.size());
// *data << uint32(Players[i].Pets[j].States.size());
// for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k)
// {
// *data << int32(Players[i].Pets[j].Abilities[k].AbilityID);
// *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining);
// *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining);
// *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex);
// *data << uint8(Players[i].Pets[j].Abilities[k].Pboid);
// }
// for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k)
// {
// *data << int32(Players[i].Pets[j].Auras[k].AbilityID);
// *data << uint32(Players[i].Pets[j].Auras[k].InstanceID);
// *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining);
// *data << int32(Players[i].Pets[j].Auras[k].CurrentRound);
// *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID);
// }
// for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k)
// {
// *data << uint32(Players[i].Pets[j].States[k].StateID);
// *data << int32(Players[i].Pets[j].States[k].StateValue);
// }
// data->WriteBits(Players[i].Pets[j].CustomName.length(), 7);
// data->FlushBits();
// data->WriteString(Players[i].Pets[j].CustomName);
// }
// }
// for (std::size_t i = 0; i < 3; ++i)
// {
// *data << uint32(Enviros[j].Auras.size());
// *data << uint32(Enviros[j].States.size());
// for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j)
// {
// *data << int32(Enviros[j].Auras[j].AbilityID);
// *data << uint32(Enviros[j].Auras[j].InstanceID);
// *data << int32(Enviros[j].Auras[j].RoundsRemaining);
// *data << int32(Enviros[j].Auras[j].CurrentRound);
// *data << uint8(Enviros[j].Auras[j].CasterPBOID);
// }
// for (std::size_t j = 0; j < Enviros[j].States.size(); ++j)
// {
// *data << uint32(Enviros[i].States[j].StateID);
// *data << int32(Enviros[i].States[j].StateValue);
// }
// }
// *data << uint16(WaitingForFrontPetsMaxSecs);
// *data << uint16(PvpMaxRoundTime);
// *data << int32(CurRound);
// *data << uint32(NpcCreatureID);
// *data << uint32(NpcDisplayID);
// *data << int8(CurPetBattleState);
// *data << uint8(ForfeitPenalty);
// *data << ObjectGuid(InitialWildPetGUID);
// data->WriteBit(IsPVP);
// data->WriteBit(CanAwardXP);
// data->FlushBits();
// }
}
if (flags.ActivePlayer)
{
Player const* player = static_cast<Player const*>(this);
bool HasSceneInstanceIDs = !player->GetSceneMgr().GetSceneTemplateByInstanceMap().empty();
bool HasRuneState = player->GetPowerIndex(POWER_RUNES) != MAX_POWERS;
data->WriteBit(HasSceneInstanceIDs);
data->WriteBit(HasRuneState);
data->FlushBits();
if (HasSceneInstanceIDs)
{
*data << uint32(player->GetSceneMgr().GetSceneTemplateByInstanceMap().size());
for (auto const& [sceneInstanceId, _] : player->GetSceneMgr().GetSceneTemplateByInstanceMap())
*data << uint32(sceneInstanceId);
}
if (HasRuneState)
{
float baseCd = float(player->GetRuneBaseCooldown());
uint32 maxRunes = uint32(player->GetMaxPower(POWER_RUNES));
*data << uint8((1 << maxRunes) - 1);
*data << uint8(player->GetRunesState());
*data << uint32(maxRunes);
for (uint32 i = 0; i < maxRunes; ++i)
*data << uint8((baseCd - float(player->GetRuneCooldown(i))) / baseCd * 255);
}
}
if (flags.Conversation)
{
Conversation const* self = static_cast<Conversation const*>(this);
if (data->WriteBit(self->GetTextureKitId() != 0))
*data << uint32(self->GetTextureKitId());
data->FlushBits();
}
}
UF::UpdateFieldFlag Object::GetUpdateFieldFlagsFor(Player const* /*target*/) const
{
return UF::UpdateFieldFlag::None;
}
void Object::BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag /*flags*/, Player const* /*target*/) const
{
*data << uint32(0);
}
void Object::AddToObjectUpdateIfNeeded()
{
if (m_inWorld && !m_objectUpdated)
m_objectUpdated = AddToObjectUpdate();
}
void Object::ClearUpdateMask(bool remove)
{
m_values.ClearChangesMask(&Object::m_objectData);
m_entityFragments.IdsChanged = false;
if (m_objectUpdated)
{
if (remove)
RemoveFromObjectUpdate();
m_objectUpdated = false;
}
}
void Object::BuildFieldsUpdate(Player* player, UpdateDataMapType& data_map) const
{
UpdateDataMapType::iterator iter = data_map.try_emplace(player, player->GetMapId()).first;
BuildValuesUpdateBlockForPlayer(&iter->second, iter->first);
BaseEntity::ClearUpdateMask(remove);
}
std::string Object::GetDebugInfo() const
{
std::stringstream sstr;
sstr << GetGUID().ToString() + " Entry " << GetEntry();
return sstr.str();
sstr << BaseEntity::GetDebugInfo() << " Entry " << GetEntry();
return std::move(sstr).str();
}
void MovementInfo::OutDebug()
+8 -388
View File
@@ -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; }
void AddToWorld() override;
void RemoveFromWorld() override;
virtual void AddToWorld();
virtual void RemoveFromWorld();
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 const* target) const;
void SendOutOfRangeForPlayer(Player const* target) const;
virtual void ClearUpdateMask(bool remove);
virtual std::string GetNameForLocaleIdx(LocaleConstant locale) const = 0;
void ClearUpdateMask(bool remove) override;
virtual bool hasQuest(uint32 /* quest_id */) const { return false; }
virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; }
void SetIsNewObject(bool enable) { m_isNewObject = enable; }
bool IsDestroyedObject() const { return m_isDestroyedObject; }
void SetDestroyedObject(bool destroyed) { m_isDestroyedObject = destroyed; }
virtual void BuildUpdate(UpdateDataMapType&) { }
void BuildFieldsUpdate(Player*, UpdateDataMapType &) const;
inline bool IsWorldObject() const { return isType(TYPEMASK_WORLDOBJECT); }
static WorldObject* ToWorldObject(Object* o) { return o ? o->ToWorldObject() : nullptr; }
static WorldObject const* ToWorldObject(Object const* o) { return o ? o->ToWorldObject() : nullptr; }
WorldObject* ToWorldObject() { if (IsWorldObject()) return reinterpret_cast<WorldObject*>(this); else return nullptr; }
WorldObject const* ToWorldObject() const { if (IsWorldObject()) return reinterpret_cast<WorldObject const*>(this); else return nullptr; }
inline bool IsItem() const { return isType(TYPEMASK_ITEM); }
static Item* ToItem(Object* o) { return o ? o->ToItem() : nullptr; }
static Item const* ToItem(Object const* o) { return o ? o->ToItem() : nullptr; }
Item* ToItem() { if (IsItem()) return reinterpret_cast<Item*>(this); else return nullptr; }
Item const* ToItem() const { if (IsItem()) return reinterpret_cast<Item const*>(this); else return nullptr; }
inline bool IsPlayer() const { return GetTypeId() == TYPEID_PLAYER; }
static Player* ToPlayer(Object* o) { return o ? o->ToPlayer() : nullptr; }
static Player const* ToPlayer(Object const* o) { return o ? o->ToPlayer() : nullptr; }
Player* ToPlayer() { if (IsPlayer()) return reinterpret_cast<Player*>(this); else return nullptr; }
Player const* ToPlayer() const { if (IsPlayer()) return reinterpret_cast<Player const*>(this); else return nullptr; }
inline bool IsCreature() const { return GetTypeId() == TYPEID_UNIT; }
static Creature* ToCreature(Object* o) { return o ? o->ToCreature() : nullptr; }
static Creature const* ToCreature(Object const* o) { return o ? o->ToCreature() : nullptr; }
Creature* ToCreature() { if (IsCreature()) return reinterpret_cast<Creature*>(this); else return nullptr; }
Creature const* ToCreature() const { if (IsCreature()) return reinterpret_cast<Creature const*>(this); else return nullptr; }
inline bool IsUnit() const { return isType(TYPEMASK_UNIT); }
static Unit* ToUnit(Object* o) { return o ? o->ToUnit() : nullptr; }
static Unit const* ToUnit(Object const* o) { return o ? o->ToUnit() : nullptr; }
Unit* ToUnit() { if (IsUnit()) return reinterpret_cast<Unit*>(this); else return nullptr; }
Unit const* ToUnit() const { if (IsUnit()) return reinterpret_cast<Unit const*>(this); else return nullptr; }
inline bool IsGameObject() const { return GetTypeId() == TYPEID_GAMEOBJECT; }
static GameObject* ToGameObject(Object* o) { return o ? o->ToGameObject() : nullptr; }
static GameObject const* ToGameObject(Object const* o) { return o ? o->ToGameObject() : nullptr; }
GameObject* ToGameObject() { if (IsGameObject()) return reinterpret_cast<GameObject*>(this); else return nullptr; }
GameObject const* ToGameObject() const { if (IsGameObject()) return reinterpret_cast<GameObject const*>(this); else return nullptr; }
inline bool IsCorpse() const { return GetTypeId() == TYPEID_CORPSE; }
static Corpse* ToCorpse(Object* o) { return o ? o->ToCorpse() : nullptr; }
static Corpse const* ToCorpse(Object const* o) { return o ? o->ToCorpse() : nullptr; }
Corpse* ToCorpse() { if (IsCorpse()) return reinterpret_cast<Corpse*>(this); else return nullptr; }
Corpse const* ToCorpse() const { if (IsCorpse()) return reinterpret_cast<Corpse const*>(this); else return nullptr; }
inline bool IsDynObject() const { return GetTypeId() == TYPEID_DYNAMICOBJECT; }
static DynamicObject* ToDynObject(Object* o) { return o ? o->ToDynObject() : nullptr; }
static DynamicObject const* ToDynObject(Object const* o) { return o ? o->ToDynObject() : nullptr; }
DynamicObject* ToDynObject() { if (IsDynObject()) return reinterpret_cast<DynamicObject*>(this); else return nullptr; }
DynamicObject const* ToDynObject() const { if (IsDynObject()) return reinterpret_cast<DynamicObject const*>(this); else return nullptr; }
inline bool IsAreaTrigger() const { return GetTypeId() == TYPEID_AREATRIGGER; }
static AreaTrigger* ToAreaTrigger(Object* o) { return o ? o->ToAreaTrigger() : nullptr; }
static AreaTrigger const* ToAreaTrigger(Object const* o) { return o ? o->ToAreaTrigger() : nullptr; }
AreaTrigger* ToAreaTrigger() { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger*>(this); else return nullptr; }
AreaTrigger const* ToAreaTrigger() const { if (IsAreaTrigger()) return reinterpret_cast<AreaTrigger const*>(this); else return nullptr; }
inline bool IsSceneObject() const { return GetTypeId() == TYPEID_SCENEOBJECT; }
static SceneObject* ToSceneObject(Object* o) { return o ? o->ToSceneObject() : nullptr; }
static SceneObject const* ToSceneObject(Object const* o) { return o ? o->ToSceneObject() : nullptr; }
SceneObject* ToSceneObject() { if (IsSceneObject()) return reinterpret_cast<SceneObject*>(this); else return nullptr; }
SceneObject const* ToSceneObject() const { if (IsSceneObject()) return reinterpret_cast<SceneObject const*>(this); else return nullptr; }
inline bool IsConversation() const { return GetTypeId() == TYPEID_CONVERSATION; }
static Conversation* ToConversation(Object* o) { return o ? o->ToConversation() : nullptr; }
static Conversation const* ToConversation(Object const* o) { return o ? o->ToConversation() : nullptr; }
Conversation* ToConversation() { if (IsConversation()) return reinterpret_cast<Conversation*>(this); else return nullptr; }
Conversation const* ToConversation() const { if (IsConversation()) return reinterpret_cast<Conversation const*>(this); else return nullptr; }
friend UF::UpdateFieldHolder;
UF::UpdateFieldHolder m_values;
UF::UpdateField<UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT> m_objectData;
template<typename T>
void ForceUpdateFieldChange(UF::UpdateFieldPrivateSetter<T> const& /*setter*/)
{
AddToObjectUpdateIfNeeded();
}
virtual std::string GetDebugInfo() const;
std::string GetDebugInfo() const override;
Trinity::unique_weak_ptr<Object> GetWeakPtr() const { return m_scriptRef; }
@@ -330,242 +173,19 @@ class TC_GAME_API Object
protected:
Object();
void _Create(ObjectGuid const& guid);
template<typename T>
void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value)
{
if (UF::SetUpdateFieldValue(setter, std::move(value)))
AddToObjectUpdateIfNeeded();
}
template<typename T>
void SetUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag)
{
static_assert(std::is_integral<T>::value, "SetUpdateFieldFlagValue must be used with integral types");
SetUpdateFieldValue(setter, setter.GetValue() | flag);
}
template<typename T>
void RemoveUpdateFieldFlagValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type flag)
{
static_assert(std::is_integral<T>::value, "RemoveUpdateFieldFlagValue must be used with integral types");
SetUpdateFieldValue(setter, setter.GetValue() & ~flag);
}
template<typename T>
typename UF::DynamicUpdateFieldSetter<T>::insert_result AddDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter)
{
AddToObjectUpdateIfNeeded();
return UF::AddDynamicUpdateFieldValue(setter);
}
template<typename T>
typename UF::DynamicUpdateFieldSetter<T>::insert_result InsertDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index)
{
AddToObjectUpdateIfNeeded();
return UF::InsertDynamicUpdateFieldValue(setter, index);
}
template<typename T>
void RemoveDynamicUpdateFieldValue(UF::DynamicUpdateFieldSetter<T> setter, uint32 index)
{
AddToObjectUpdateIfNeeded();
UF::RemoveDynamicUpdateFieldValue(setter, index);
}
template<typename K, typename V>
void RemoveMapUpdateFieldValue(UF::MapUpdateFieldSetter<K, V> setter, std::type_identity_t<K> const& key)
{
AddToObjectUpdateIfNeeded();
UF::RemoveMapUpdateFieldValue(setter, key);
}
template<typename T>
void ClearDynamicUpdateFieldValues(UF::DynamicUpdateFieldSetter<T> setter)
{
AddToObjectUpdateIfNeeded();
UF::ClearDynamicUpdateFieldValues(setter);
}
template<typename T>
void RemoveOptionalUpdateFieldValue(UF::OptionalUpdateFieldSetter<T> setter)
{
AddToObjectUpdateIfNeeded();
UF::RemoveOptionalUpdateFieldValue(setter);
}
// stat system helpers
template<typename T>
void SetUpdateFieldStatValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type value)
{
static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types");
SetUpdateFieldValue(setter, std::max(value, T(0)));
}
template<typename T>
void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, typename UF::UpdateFieldPrivateSetter<T>::value_type mod, bool apply)
{
static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types");
T value = setter.GetValue();
if (apply)
value += mod;
else
value -= mod;
SetUpdateFieldValue(setter, value);
}
template<typename T>
void ApplyPercentModUpdateFieldValue(UF::UpdateFieldPrivateSetter<T> setter, float percent, bool apply)
{
static_assert(std::is_arithmetic<T>::value, "SetUpdateFieldStatValue must be used with arithmetic types");
T value = setter.GetValue();
// don't want to include Util.h here
//ApplyPercentModFloatVar(value, percent, apply);
if (percent == -100.0f)
percent = -99.99f;
value *= (apply ? (100.0f + percent) / 100.0f : 100.0f / (100.0f + percent));
SetUpdateFieldValue(setter, value);
}
template<typename Action>
void DoWithSuppressingObjectUpdates(Action&& action)
{
bool wasUpdatedBeforeAction = m_objectUpdated;
action();
if (m_objectUpdated && !wasUpdatedBeforeAction)
{
RemoveFromObjectUpdate();
m_objectUpdated = false;
}
}
void BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player const* target) const;
virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const;
virtual void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0;
virtual void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const = 0;
static void BuildEntityFragments(ByteBuffer* data, std::span<WowCS::EntityFragment const> fragments);
void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0;
void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override = 0;
void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer* data, EnumFlag<UF::UpdateFieldFlag> flags) const;
public:
virtual void BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const;
protected:
TypeID m_objectTypeId;
CreateObjectBits m_updateFlag;
WowCS::EntityFragmentsHolder m_entityFragments;
virtual bool AddToObjectUpdate() = 0;
virtual void RemoveFromObjectUpdate() = 0;
void AddToObjectUpdateIfNeeded();
bool m_objectUpdated;
private:
bool m_inWorld;
bool m_isNewObject;
bool m_isDestroyedObject;
struct NoopObjectDeleter { void operator()(Object*) const { /*noop - not managed*/ } };
Trinity::unique_trackable_ptr<Object> m_scriptRef;
Object(Object const& right) = delete;
Object(Object&& right) = delete;
Object& operator=(Object const& right) = delete;
Object& operator=(Object&& right) = delete;
};
inline Object* UF::UpdateFieldHolder::GetOwner()
{
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#endif
return reinterpret_cast<Object*>(reinterpret_cast<std::byte*>(this) - offsetof(Object, m_values));
#if TRINITY_COMPILER == TRINITY_COMPILER_GNU
#pragma GCC diagnostic pop
#endif
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(UpdateField<T, BlockBit, Bit> Derived::* field)
{
Object* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
_changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
return { (static_cast<Derived*>(owner)->*field)._value };
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline UF::OptionalUpdateFieldSetter<T> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field)
{
Object* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
_changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
return { static_cast<Derived*>(owner)->*field };
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline UF::MutableFieldReference<T, false> UF::UpdateFieldHolder::ModifyValue(OptionalUpdateField<T, BlockBit, Bit> Derived::* field, uint32 /*dummy*/)
{
Object* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask |= owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
_changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit);
auto& uf = (static_cast<Derived*>(owner)->*field);
if (!uf.has_value())
uf.ConstructValue();
return { *uf._value };
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline void UF::UpdateFieldHolder::ClearChangesMask(UpdateField<T, BlockBit, Bit> Derived::* field)
{
Object* owner = GetOwner();
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
{
_changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit);
if (!_changesMask)
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
}
else
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
(static_cast<Derived*>(owner)->*field)._value.ClearChangesMask();
}
template <typename Derived, typename T, int32 BlockBit, uint32 Bit>
inline void UF::UpdateFieldHolder::ClearChangesMask(OptionalUpdateField<T, BlockBit, Bit> Derived::* field)
{
Object* owner = GetOwner();
if constexpr (WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject)
{
_changesMask &= ~UpdateMaskHelpers::GetBlockFlag(Bit);
if (!_changesMask)
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
}
else
owner->m_entityFragments.ContentsChangedMask &= ~owner->m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit));
auto& uf = (static_cast<Derived*>(owner)->*field);
if (uf.has_value())
uf._value->ClearChangesMask();
}
template <class T_VALUES, class T_FLAGS, class FLAG_TYPE, size_t ARRAY_SIZE>
class FlaggedValuesArray32
{
+2 -1
View File
@@ -81,7 +81,7 @@ enum TypeMask
TYPEMASK_WORLDOBJECT = TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT | TYPEMASK_CORPSE | TYPEMASK_AREATRIGGER | TYPEMASK_SCENEOBJECT | TYPEMASK_CONVERSATION
};
inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES> ObjectTypeMask =
inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES + 1> ObjectTypeMask =
{
TYPEMASK_OBJECT,
TYPEMASK_OBJECT | TYPEMASK_ITEM,
@@ -101,6 +101,7 @@ inline constexpr std::array<uint32, NUM_CLIENT_OBJECT_TYPES> ObjectTypeMask =
TYPEMASK_OBJECT | TYPEMASK_AI_GROUP,
TYPEMASK_OBJECT | TYPEMASK_SCENARIO,
TYPEMASK_OBJECT | TYPEMASK_LOOT_OBJECT,
0,
};
enum class HighGuid
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -387,7 +387,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac
//FIXME: outfitId not used in player creating
/// @todo need more checks against packet modifications
Object::_Create(ObjectGuid::Create<HighGuid::Player>(guidlow));
_Create(ObjectGuid::Create<HighGuid::Player>(guidlow));
m_name = createInfo->Name;
@@ -17856,7 +17856,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
return false;
}
Object::_Create(guid);
_Create(guid);
m_name = std::move(fields.name);
@@ -24097,7 +24097,7 @@ uint8 Player::GetStartLevel(uint8 race, uint8 playerClass, Optional<int32> chara
return startLevel;
}
bool Player::HaveAtClient(Object const* u) const
bool Player::HaveAtClient(BaseEntity const* u) const
{
return u == this || m_clientGUIDs.find(u->GetGUID()) != m_clientGUIDs.end();
}
+1 -1
View File
@@ -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;
@@ -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();
@@ -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)