From e5d59d5f980e6d0829c682ea4a3ad4ae36389ae2 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Fri, 26 Apr 2019 18:41:31 +0200 Subject: [PATCH] Core/Object: Range check (#23179) (ported commit: 32e1de39a26628dcb64bc21ad415afb2ad938925) --- .../game/Entities/GameObject/GameObject.cpp | 172 ++++++++++++++++-- .../game/Entities/GameObject/GameObject.h | 17 +- src/server/game/Entities/Object/Object.cpp | 2 +- .../game/Entities/Transport/Transport.cpp | 4 +- src/server/game/Spells/Spell.cpp | 8 +- src/server/scripts/Commands/cs_gobject.cpp | 2 +- 6 files changed, 169 insertions(+), 36 deletions(-) diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 5fd052f1d62..8125ab1725c 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -41,6 +41,8 @@ #include "Transport.h" #include "UpdateFieldFlags.h" #include "World.h" +#include +#include #include bool QuaternionData::isUnit() const @@ -246,7 +248,7 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u return false; } - SetWorldRotation(rotation.x, rotation.y, rotation.z, rotation.w); + SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w); GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId()); // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation @@ -868,7 +870,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.id = GetEntry(); data.spawnPoint.WorldRelocate(this); data.phaseMask = phaseMask; - data.rotation = m_worldRotation; + data.rotation = m_localRotation; data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; data.animprogress = GetGoAnimProgress(); data.goState = GetGoState(); @@ -896,10 +898,10 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) stmt->setFloat(index++, GetPositionY()); stmt->setFloat(index++, GetPositionZ()); stmt->setFloat(index++, GetOrientation()); - stmt->setFloat(index++, m_worldRotation.x); - stmt->setFloat(index++, m_worldRotation.y); - stmt->setFloat(index++, m_worldRotation.z); - stmt->setFloat(index++, m_worldRotation.w); + stmt->setFloat(index++, m_localRotation.x); + stmt->setFloat(index++, m_localRotation.y); + stmt->setFloat(index++, m_localRotation.z); + stmt->setFloat(index++, m_localRotation.w); stmt->setInt32(index++, int32(m_respawnDelayTime)); stmt->setUInt8(index++, GetGoAnimProgress()); stmt->setUInt8(index++, uint8(GetGoState())); @@ -2030,21 +2032,21 @@ void GameObject::UpdatePackedRotation() static const int32 PACK_YZ_MASK = (PACK_YZ << 1) - 1; static const int32 PACK_X_MASK = (PACK_X << 1) - 1; - int8 w_sign = (m_worldRotation.w >= 0.f ? 1 : -1); - int64 x = int32(m_worldRotation.x * PACK_X) * w_sign & PACK_X_MASK; - int64 y = int32(m_worldRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK; - int64 z = int32(m_worldRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK; + 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; m_packedRotation = z | (y << 21) | (x << 42); } -void GameObject::SetWorldRotation(float qx, float qy, float qz, float qw) +void GameObject::SetLocalRotation(float qx, float qy, float qz, float qw) { G3D::Quat rotation(qx, qy, qz, qw); rotation.unitize(); - m_worldRotation.x = rotation.x; - m_worldRotation.y = rotation.y; - m_worldRotation.z = rotation.z; - m_worldRotation.w = rotation.w; + m_localRotation.x = rotation.x; + m_localRotation.y = rotation.y; + m_localRotation.z = rotation.z; + m_localRotation.w = rotation.w; UpdatePackedRotation(); } @@ -2056,10 +2058,27 @@ void GameObject::SetParentRotation(QuaternionData const& rotation) SetFloatValue(GAMEOBJECT_PARENTROTATION + 3, rotation.w); } -void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot) +void GameObject::SetLocalRotationAngles(float z_rot, float y_rot, float x_rot) { G3D::Quat quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot)); - SetWorldRotation(quat.x, quat.y, quat.z, quat.w); + SetLocalRotation(quat.x, quat.y, quat.z, quat.w); +} + +QuaternionData GameObject::GetWorldRotation() const +{ + QuaternionData localRotation = GetLocalRotation(); + if (Transport* transport = GetTransport()) + { + QuaternionData worldRotation = transport->GetWorldRotation(); + + 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; + + return QuaternionData(resultRotation.x, resultRotation.y, resultRotation.z, resultRotation.w); + } + return localRotation; } void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/) @@ -2453,9 +2472,6 @@ float GameObject::GetInteractionDistance() const case GAMEOBJECT_TYPE_FISHINGHOLE: case GAMEOBJECT_TYPE_FISHINGNODE: return 20.0f + CONTACT_DISTANCE; // max spell range - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_QUESTGIVER: - return INTERACTION_DISTANCE + GetFloatValue(OBJECT_FIELD_SCALE_X); default: return INTERACTION_DISTANCE; } @@ -2564,3 +2580,119 @@ MapTransport const* GameObject::ToMapTransport() const return nullptr; } + +bool GameObject::IsAtInteractDistance(Player const* player, SpellInfo const* spell) const +{ + if (spell || (spell = GetSpellForLock(player))) + { + float maxRange = spell->GetMaxRange(spell->IsPositive()); + + if (GetGoType() == GAMEOBJECT_TYPE_SPELL_FOCUS) + return maxRange * maxRange >= GetExactDistSq(player); + + if (GameObjectDisplayInfoEntry const* displayInfo = sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId)) + return IsAtInteractDistance(*player, maxRange); + } + + float distance; + switch (GetGoType()) + { + case GAMEOBJECT_TYPE_AREADAMAGE: + distance = 0.0f; + break; + case GAMEOBJECT_TYPE_QUESTGIVER: + case GAMEOBJECT_TYPE_TEXT: + case GAMEOBJECT_TYPE_FLAGSTAND: + case GAMEOBJECT_TYPE_FLAGDROP: + case GAMEOBJECT_TYPE_MINI_GAME: + distance = 5.5555553f; + break; + case GAMEOBJECT_TYPE_BINDER: + distance = 10.0f; + break; + case GAMEOBJECT_TYPE_CHAIR: + case GAMEOBJECT_TYPE_BARBER_CHAIR: + distance = 3.0f; + break; + case GAMEOBJECT_TYPE_FISHINGNODE: + distance = 100.0f; + break; + case GAMEOBJECT_TYPE_CAMERA: + case GAMEOBJECT_TYPE_MAP_OBJECT: + case GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY: + case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: + case GAMEOBJECT_TYPE_DOOR: + default: + distance = 5.0f; + break; + + // Following values are not blizzlike + case GAMEOBJECT_TYPE_MAILBOX: + // Successful mailbox interaction is rather critical to the client, failing it will start a minute-long cooldown until the next mail query may be executed. + // And since movement info update is not sent with mailbox interaction query, server may find the player outside of interaction range. Thus we increase it. + distance = 10.0f; // 5.0f is blizzlike + break; + } + + return IsAtInteractDistance(*player, distance); +} + +bool GameObject::IsAtInteractDistance(Position const& pos, float radius) const +{ + if (GameObjectDisplayInfoEntry const* displayInfo = sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId)) + { + float scale = GetObjectScale(); + + float minX = displayInfo->minX * scale - radius; + float minY = displayInfo->minY * scale - radius; + float minZ = displayInfo->minZ * scale - radius; + float maxX = displayInfo->maxX * scale + radius; + float maxY = displayInfo->maxY * scale + radius; + float maxZ = displayInfo->maxZ * scale + radius; + + QuaternionData localRotation = GetLocalRotation(); + G3D::Quat localRotationQuat(localRotation.x, localRotation.y, localRotation.z, localRotation.w); + + return G3D::CoordinateFrame { { localRotationQuat }, { GetPositionX(), GetPositionY(), GetPositionZ() } } + .toWorldSpace(G3D::Box { { minX, minY, minZ }, { maxX, maxY, maxZ } }) + .contains({ pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() }); + } + + return GetExactDist(&pos) <= radius; +} + +SpellInfo const* GameObject::GetSpellForLock(Player const* player) const +{ + if (!player) + return nullptr; + + uint32 lockId = GetGOInfo()->GetLockId(); + if (!lockId) + return nullptr; + + LockEntry const* lock = sLockStore.LookupEntry(lockId); + if (!lock) + return nullptr; + + for (uint8 i = 0; i < MAX_LOCK_CASE; ++i) + { + if (!lock->Type[i]) + continue; + + if (lock->Type[i] == LOCK_KEY_SKILL) + if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(lock->Index[i])) + return spell; + + if (lock->Type[i] != LOCK_KEY_SKILL) + break; + + for (auto&& playerSpell : player->GetSpellMap()) + if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(playerSpell.first)) + for (auto&& effect : spell->Effects) + if (effect.Effect == SPELL_EFFECT_OPEN_LOCK && ((uint32) effect.MiscValue) == lock->Index[i]) + if (effect.CalcValue(player) >= int32(lock->Skill[i])) + return spell; + } + + return nullptr; +} diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index c147e6ecacb..5b844d77e4f 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -103,11 +103,13 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } // z_rot, y_rot, x_rot - rotation angles around z, y and x axes - void SetWorldRotationAngles(float z_rot, float y_rot, float x_rot); - void SetWorldRotation(float qx, float qy, float qz, float qw); + void SetLocalRotationAngles(float z_rot, float y_rot, float x_rot); + void SetLocalRotation(float qx, float qy, float qz, float qw); void SetParentRotation(QuaternionData const& rotation); // transforms(rotates) transport's path - QuaternionData const& GetWorldRotation() const { return m_worldRotation; } - int64 GetPackedWorldRotation() const { return m_packedRotation; } + QuaternionData const& GetLocalRotation() const { return m_localRotation; } + int64 GetPackedLocalRotation() const { return m_packedRotation; } + + QuaternionData GetWorldRotation() const; // overwrite WorldObject function for proper name localization std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override; @@ -286,6 +288,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void UpdateModelPosition(); + bool IsAtInteractDistance(Position const& pos, float radius) const; + bool IsAtInteractDistance(Player const* player, SpellInfo const* spell) const; + + SpellInfo const* GetSpellForLock(Player const* player) const; + void AIM_Destroy(); bool AIM_Initialize(); @@ -322,7 +329,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject GameObjectValue m_goValue; int64 m_packedRotation; - QuaternionData m_worldRotation; + QuaternionData m_localRotation; 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 a1de0257e53..c903ad73d7a 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -600,7 +600,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const } if (flags & UPDATEFLAG_ROTATION) - *data << uint64(ToGameObject()->GetPackedWorldRotation()); + *data << uint64(ToGameObject()->GetPackedLocalRotation()); if (flags & UPDATEFLAG_UNK5) { diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 8ed578ef6ab..3f09b2690d5 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -177,7 +177,7 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, Map* map, uint GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId()); - SetWorldRotation(rotation.x, rotation.y, rotation.z, rotation.w); + SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w); // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation QuaternionData parentRotation; @@ -583,7 +583,7 @@ bool MapTransport::CreateMapTransport(ObjectGuid::LowType guidlow, uint32 entry, m_goValue.Transport.PathProgress = 0; SetPeriod(tInfo->pathTime); SetGoState(!m_goInfo->moTransport.canBeStopped ? GO_STATE_READY : GO_STATE_ACTIVE); - SetWorldRotation(0.0f, 0.0f, 0.0f, 1.0f); + SetLocalRotation(0.0f, 0.0f, 0.0f, 1.0f); SetParentRotation(QuaternionData()); m_model = CreateModel(); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8df48d80ce2..a657c36297a 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -6439,9 +6439,6 @@ SpellCastResult Spell::CheckRange(bool strict) const if (m_spellInfo->RangeEntry && m_spellInfo->RangeEntry->type != SPELL_RANGE_MELEE && !strict) maxRange += std::min(MAX_SPELL_RANGE_TOLERANCE, maxRange*0.1f); // 10% but no more than MAX_SPELL_RANGE_TOLERANCE - // save the original values before squaring them - float origMinRange = minRange, origMaxRange = maxRange; - // get square values for sqr distance checks minRange *= minRange; maxRange *= maxRange; @@ -6463,10 +6460,7 @@ SpellCastResult Spell::CheckRange(bool strict) const if (GameObject* goTarget = m_targets.GetGOTarget()) { - if (origMinRange > 0.0f && goTarget->IsInRange(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), origMinRange)) - return SPELL_FAILED_OUT_OF_RANGE; - - if (!goTarget->IsInRange(m_caster->GetPositionX(), m_caster->GetPositionY(), m_caster->GetPositionZ(), origMaxRange)) + if (!goTarget->IsAtInteractDistance(m_caster->ToPlayer(), m_spellInfo)) return SPELL_FAILED_OUT_OF_RANGE; } diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 8b0f00f5dae..8053aac8ebd 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -445,7 +445,7 @@ public: } object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); - object->SetWorldRotationAngles(oz, oy, ox); + object->SetLocalRotationAngles(oz, oy, ox); object->SaveToDB(); handler->PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, object->GetGUID().GetCounter(), object->GetGOInfo()->name.c_str(), object->GetGUID().GetCounter(), oz);