From 9b63cde7cbfcf3dc6a80e3412c63162b155b5cfc Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:41:08 -0600 Subject: [PATCH] fix(DB/Gameobject): Recalculate quaternion rotation values from orientation (#24617) --- .../rev_1770209139010970104.sql | 2 + .../rev_1770210647797541306.sql | 67 +++++++++++++++++++ src/server/game/Battlefield/Battlefield.cpp | 3 +- .../game/Entities/GameObject/GameObject.cpp | 54 +++++++-------- .../game/Entities/GameObject/GameObject.h | 12 ++-- src/server/game/Entities/Object/Object.cpp | 2 +- .../game/Entities/Transport/Transport.cpp | 4 +- src/server/scripts/Commands/cs_gobject.cpp | 2 +- 8 files changed, 105 insertions(+), 41 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1770209139010970104.sql create mode 100644 data/sql/updates/pending_db_world/rev_1770210647797541306.sql diff --git a/data/sql/updates/pending_db_world/rev_1770209139010970104.sql b/data/sql/updates/pending_db_world/rev_1770209139010970104.sql new file mode 100644 index 000000000..453b1e3b1 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1770209139010970104.sql @@ -0,0 +1,2 @@ +-- Recalculate quaternion rotation from orientation for unverified gameobjects +UPDATE `gameobject` SET `rotation2` = SIN(`orientation` / 2), `rotation3` = COS(`orientation` / 2) WHERE `rotation0` = 0 AND `rotation1` = 0 AND (`VerifiedBuild` IS NULL OR `VerifiedBuild` = 0); diff --git a/data/sql/updates/pending_db_world/rev_1770210647797541306.sql b/data/sql/updates/pending_db_world/rev_1770210647797541306.sql new file mode 100644 index 000000000..4bea5e778 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1770210647797541306.sql @@ -0,0 +1,67 @@ +-- Add transport parent_rotation values (from TrinityCore/UDB) +DELETE FROM `gameobject_addon` WHERE `guid` IN (14139,16760,16761,16871,20505,24055,24074,24075,25138,34057,35693,35694,55230,56744,56937,56954,56961,57132,57133,57140,57141,57799,57992,57993,57994,57995,57996,58299,58304,58305,58306,58310,58782,58824,58935,59160,59328,59336,59343,59350,59386,59762,59763,59764,59765,59766,59779,59780,59781,59782,59783,60421,60463,61053,65435,65436,65438,65439,65520,66717,67873,67874,67875,67876); +INSERT INTO `gameobject_addon` (`guid`,`parent_rotation0`,`parent_rotation1`,`parent_rotation2`,`parent_rotation3`,`invisibilityType`,`invisibilityValue`) VALUES +(14139,0,0,1,-0.0000000437114,0,0), +(16760,0,0,-0.378575,0.92557,0,0), +(16761,0,0,-0.378575,0.92557,0,0), +(16871,0,0,0.694658,0.71934,0,0), +(20505,0,0,-0.694658,0.71934,0,0), +(24055,0,0,-0.526214,0.850352,0,0), +(24074,0,0,-0.526214,0.850352,0,0), +(24075,0,0,-0.526214,0.850352,0,0), +(25138,0,0,0.45399,0.891007,0,0), +(34057,0,0,0.000000325841,1,0,0), +(35693,0,0,0.989651,0.143493,0,0), +(35694,0,0,0.989651,0.143493,0,0), +(55230,0,0,0.999048,0.0436193,0,0), +(56744,0,0,0.951057,0.309017,0,0), +(56937,0,0,0.951057,0.309017,0,0), +(56954,0,0,0.951057,0.309017,0,0), +(56961,0,0,0.999048,0.0436193,0,0), +(57132,-0.00276125,-0.00551835,-0.370553,0.928791,0,0), +(57133,0.00544418,-0.00290476,0.918772,0.394739,0,0), +(57140,-0.00276125,-0.00551835,-0.370553,0.928791,0,0), +(57141,0.00544418,-0.00290476,0.918772,0.394739,0,0), +(57799,0,0,0.999048,0.0436193,0,0), +(57992,0,0,-0.370557,0.92881,0,0), +(57993,0,0,-0.760406,0.649448,0,0), +(57994,0,0,0.915312,0.402747,0,0), +(57995,0,0,-0.748956,0.66262,0,0), +(57996,0,0,0.995805,0.0915015,0,0), +(58299,0,0,0.999048,0.0436193,0,0), +(58304,0,0,-0.263031,0.964787,0,0), +(58305,0,0,0.522499,0.85264,0,0), +(58306,0,0,0.996917,-0.0784592,0,0), +(58310,0,0,0.333807,0.942641,0,0), +(58782,0,0,0.333807,0.942641,0,0), +(58824,0,0,0.333807,0.942641,0,0), +(58935,0,0,0.932008,-0.362438,0,0), +(59160,0,0,0.99999,0.00436324,0,0), +(59328,0,0,0.99999,-0.00436333,0,0), +(59336,0,0,0.99999,-0.00436333,0,0), +(59343,0,0,0.99999,-0.00436333,0,0), +(59350,0,0,0.99999,-0.00436333,0,0), +(59386,0,0,0.99999,-0.00436333,0,0), +(59762,0,0,-0.370557,0.92881,0,0), +(59763,0,0,-0.760406,0.649448,0,0), +(59764,0,0,0.915312,0.402747,0,0), +(59765,0,0,-0.748956,0.66262,0,0), +(59766,0,0,0.995805,0.0915015,0,0), +(59779,0,0,-0.370557,0.92881,0,0), +(59780,0,0,-0.760406,0.649448,0,0), +(59781,0,0,0.915312,0.402747,0,0), +(59782,0,0,-0.748956,0.66262,0,0), +(59783,0,0,0.995805,0.0915015,0,0), +(60421,0,0,0.99999,0.00436324,0,0), +(60463,0,0,0.999048,0.0436193,0,0), +(61053,0,0,0.999048,0.0436193,0,0), +(65435,0,0,0.915312,0.402747,0,0), +(65436,0,0,0.99999,0.00436324,0,0), +(65438,0,0,0.915312,0.402747,0,0), +(65439,0,0,0.915312,0.402747,0,0), +(65520,0,0,0.99999,0.00436324,0,0), +(66717,0,0,0.999657,0.0261769,0,0), +(67873,-0.00276125,-0.00551835,-0.370553,0.928791,0,0), +(67874,0.00544418,-0.00290476,0.918772,0.394739,0,0), +(67875,-0.00276125,-0.00551835,-0.370553,0.928791,0,0), +(67876,0.00544418,-0.00290476,0.918772,0.394739,0,0); diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 9ad1a9c9a..7a52fb9c1 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -840,7 +840,8 @@ GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z // Create gameobject GameObject* go = sObjectMgr->IsGameObjectStaticTransport(entry) ? new StaticTransport() : new GameObject(); - if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, x, y, z, o, G3D::Quat(), 100, GO_STATE_READY)) + G3D::Quat rotation = G3D::Quat::fromAxisAngleRotation(G3D::Vector3::unitZ(), o); + if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, x, y, z, o, rotation, 100, GO_STATE_READY)) { LOG_ERROR("sql.sql", "Battlefield::SpawnGameObject: Gameobject template {} not found in database! Battlefield not created!", entry); LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Cannot create gameobject template {}! Battlefield not created!", entry); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index d093eb4a1..fd6049057 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -298,7 +298,7 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u return false; } - SetLocalRotation(rotation); + SetWorldRotation(rotation); GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId()); QuaternionData parentRotation; @@ -1038,7 +1038,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool data.posY = GetPositionY(); data.posZ = GetPositionZ(); data.orientation = GetOrientation(); - data.rotation = m_localRotation; + data.rotation = WorldRotation; data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; data.animprogress = GetGoAnimProgress(); data.go_state = GetGoState(); @@ -1064,10 +1064,10 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool stmt->SetData(index++, GetPositionY()); stmt->SetData(index++, GetPositionZ()); stmt->SetData(index++, GetOrientation()); - stmt->SetData(index++, m_localRotation.x); - stmt->SetData(index++, m_localRotation.y); - stmt->SetData(index++, m_localRotation.z); - stmt->SetData(index++, m_localRotation.w); + stmt->SetData(index++, WorldRotation.x); + stmt->SetData(index++, WorldRotation.y); + stmt->SetData(index++, WorldRotation.z); + stmt->SetData(index++, WorldRotation.w); stmt->SetData(index++, int32(m_respawnDelayTime)); stmt->SetData(index++, GetGoAnimProgress()); stmt->SetData(index++, uint8(GetGoState())); @@ -2199,24 +2199,18 @@ void GameObject::UpdatePackedRotation() static const int32 PACK_X = PACK_YZ << 1; static const int32 PACK_YZ_MASK = (PACK_YZ << 1) - 1; static const int32 PACK_X_MASK = (PACK_X << 1) - 1; - int8 w_sign = (m_localRotation.w >= 0.f ? 1 : -1); - int64 x = int32(m_localRotation.x * PACK_X) * w_sign & PACK_X_MASK; - int64 y = int32(m_localRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK; - int64 z = int32(m_localRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK; + int8 w_sign = (WorldRotation.w >= 0.f ? 1 : -1); + int64 x = int32(WorldRotation.x * PACK_X) * w_sign & PACK_X_MASK; + int64 y = int32(WorldRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK; + int64 z = int32(WorldRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK; m_packedRotation = z | (y << 21) | (x << 42); } -void GameObject::SetLocalRotation(G3D::Quat const& rot) +void GameObject::SetWorldRotation(G3D::Quat const& rot) { - G3D::Quat rotation; - // Temporary solution for gameobjects that have no rotation data in DB: - if (G3D::fuzzyEq(rot.z, 0.f) && G3D::fuzzyEq(rot.w, 0.f)) - rotation = G3D::Quat::fromAxisAngleRotation(G3D::Vector3::unitZ(), GetOrientation()); - else - rotation = rot; - + G3D::Quat rotation = rot; rotation.unitize(); - m_localRotation = rotation; + WorldRotation = rotation; UpdatePackedRotation(); } @@ -2228,26 +2222,26 @@ void GameObject::SetTransportPathRotation(float qx, float qy, float qz, float qw SetFloatValue(GAMEOBJECT_PARENTROTATION + 3, qw); } -void GameObject::SetLocalRotationAngles(float z_rot, float y_rot, float x_rot) +void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot) { - SetLocalRotation(G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot))); + SetWorldRotation(G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot))); } -G3D::Quat GameObject::GetWorldRotation() const +G3D::Quat GameObject::GetFinalWorldRotation() const { - G3D::Quat localRotation = GetLocalRotation(); + G3D::Quat worldRotation = GetWorldRotation(); if (Transport* transport = GetTransport()) { - G3D::Quat worldRotation = transport->GetWorldRotation(); + G3D::Quat transportRotation = transport->GetWorldRotation(); + G3D::Quat transportRotationQuat(transportRotation.x, transportRotation.y, transportRotation.z, transportRotation.w); G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.w); - G3D::Quat localRotationQuat(localRotation.x, localRotation.y, localRotation.z, localRotation.w); - G3D::Quat resultRotation = localRotationQuat * worldRotationQuat; + G3D::Quat resultRotation = worldRotationQuat * transportRotationQuat; return G3D::Quat(resultRotation.x, resultRotation.y, resultRotation.z, resultRotation.w); } - return localRotation; + return worldRotation; } void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/) @@ -2967,10 +2961,10 @@ bool GameObject::IsAtInteractDistance(Position const& pos, float radius) const float maxY = displayInfo->maxY * scale + radius; float maxZ = displayInfo->maxZ * scale + radius; - G3D::Quat worldRotation = GetWorldRotation(); - G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.w); + G3D::Quat finalRotation = GetFinalWorldRotation(); + G3D::Quat finalRotationQuat(finalRotation.x, finalRotation.y, finalRotation.z, finalRotation.w); - return G3D::CoordinateFrame {{worldRotationQuat}, {GetPositionX(), GetPositionY(), GetPositionZ()}}.toWorldSpace(G3D::Box {{minX, minY, minZ}, {maxX, maxY, maxZ}}).contains({pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()}); + return G3D::CoordinateFrame {{finalRotationQuat}, {GetPositionX(), GetPositionY(), GetPositionZ()}}.toWorldSpace(G3D::Box {{minX, minY, minZ}, {maxX, maxY, maxZ}}).contains({pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()}); } return GetExactDist(&pos) <= radius; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 12154547e..efcb66249 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -144,12 +144,12 @@ public: [[nodiscard]] ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } // z_rot, y_rot, x_rot - rotation angles around z, y and x axes - void SetLocalRotationAngles(float z_rot, float y_rot, float x_rot); - void SetLocalRotation(G3D::Quat const& rot); + void SetWorldRotationAngles(float z_rot, float y_rot, float x_rot); + void SetWorldRotation(G3D::Quat const& rot); void SetTransportPathRotation(float qx, float qy, float qz, float qw); - [[nodiscard]] G3D::Quat const& GetLocalRotation() const { return m_localRotation; } - [[nodiscard]] int64 GetPackedLocalRotation() const { return m_packedRotation; } - [[nodiscard]] G3D::Quat GetWorldRotation() const; + [[nodiscard]] G3D::Quat const& GetWorldRotation() const { return WorldRotation; } + [[nodiscard]] int64 GetPackedWorldRotation() const { return m_packedRotation; } + [[nodiscard]] G3D::Quat GetFinalWorldRotation() const; // overwrite WorldObject function for proper name localization [[nodiscard]] std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override; @@ -394,7 +394,7 @@ protected: bool m_allowModifyDestructibleBuilding; int64 m_packedRotation; - G3D::Quat m_localRotation; + G3D::Quat WorldRotation; Position m_stationaryPosition; ObjectGuid m_lootRecipient; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 73ced5874..8df600651 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -479,7 +479,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x200 if (flags & UPDATEFLAG_ROTATION) { - *data << int64(ToGameObject()->GetPackedLocalRotation()); + *data << int64(ToGameObject()->GetPackedWorldRotation()); } } diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index cb1987920..dbe068d14 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -99,7 +99,7 @@ bool MotionTransport::CreateMoTrans(ObjectGuid::LowType guidlow, uint32 entry, u SetName(goinfo->name); // pussywizard: no WorldRotation for MotionTransports - SetLocalRotation(G3D::Quat()); + SetWorldRotation(G3D::Quat()); // pussywizard: no PathRotation for MotionTransports SetTransportPathRotation(0.0f, 0.0f, 0.0f, 1.0f); @@ -768,7 +768,7 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m // pussywizard: temporarily calculate WorldRotation from orientation, do so until values in db are correct //SetWorldRotation( /*for StaticTransport we need 2 rotation Quats in db for World- and Path- Rotation*/ ); - SetLocalRotationAngles(NormalizeOrientation(GetOrientation()), 0.0f, 0.0f); + SetWorldRotationAngles(NormalizeOrientation(GetOrientation()), 0.0f, 0.0f); // pussywizard: PathRotation for StaticTransport (only StaticTransports have PathRotation) SetTransportPathRotation(rotation.x, rotation.y, rotation.z, rotation.w); diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index e4f1615b9..e41d93d07 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -332,7 +332,7 @@ public: Map* map = object->GetMap(); object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), *oz); - object->SetLocalRotationAngles(*oz, oy.value_or(0.0f), ox.value_or(0.0f)); + object->SetWorldRotationAngles(*oz, oy.value_or(0.0f), ox.value_or(0.0f)); object->SaveToDB(); // Generate a completely new spawn with new guid