From 69907a6d6de5587992e423d123387d9552b81f45 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Sun, 5 May 2019 00:34:40 +0200 Subject: [PATCH] * Core/Object: Range check vol. 2 (#23226) (ported commit: 78070163dc6b9bb34ab2d006e9cc218548e861b7) * Core/Object: Fix all missing parts for #23062 --- .../game/Entities/GameObject/GameObject.cpp | 93 ++++++++----------- .../game/Entities/GameObject/GameObject.h | 5 +- src/server/game/Entities/Player/Player.cpp | 4 +- src/server/game/Handlers/LootHandler.cpp | 6 +- src/server/game/Handlers/SpellHandler.cpp | 38 +++++--- src/server/game/Miscellaneous/SharedDefines.h | 3 +- src/server/game/Spells/Spell.cpp | 5 + 7 files changed, 82 insertions(+), 72 deletions(-) diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 8125ab1725..14c62ad733 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -2464,14 +2464,35 @@ float GameObject::GetInteractionDistance() const { switch (GetGoType()) { - /// @todo find out how the client calculates the maximal usage distance to spellless working - // gameobjects like guildbanks and mailboxes - 10.0 is a just an abitrary choosen number + case GAMEOBJECT_TYPE_AREADAMAGE: + return 0.0f; + case GAMEOBJECT_TYPE_QUESTGIVER: + case GAMEOBJECT_TYPE_TEXT: + case GAMEOBJECT_TYPE_FLAGSTAND: + case GAMEOBJECT_TYPE_FLAGDROP: + case GAMEOBJECT_TYPE_MINI_GAME: + return 5.5555553f; + case GAMEOBJECT_TYPE_BINDER: + return 10.0f; + case GAMEOBJECT_TYPE_CHAIR: + case GAMEOBJECT_TYPE_BARBER_CHAIR: + return 3.0f; + case GAMEOBJECT_TYPE_FISHINGNODE: + return 100.0f; + case GAMEOBJECT_TYPE_FISHINGHOLE: + return 20.0f + CONTACT_DISTANCE; // max spell range + case GAMEOBJECT_TYPE_CAMERA: + case GAMEOBJECT_TYPE_MAP_OBJECT: + case GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY: + case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: + case GAMEOBJECT_TYPE_DOOR: + return 5.0f; + // Following values are not blizzlike case GAMEOBJECT_TYPE_GUILD_BANK: case GAMEOBJECT_TYPE_MAILBOX: - return 10.0f; - case GAMEOBJECT_TYPE_FISHINGHOLE: - case GAMEOBJECT_TYPE_FISHINGNODE: - return 20.0f + CONTACT_DISTANCE; // max spell range + // 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. + return 10.0f; // 5.0f is blizzlike default: return INTERACTION_DISTANCE; } @@ -2594,47 +2615,7 @@ bool GameObject::IsAtInteractDistance(Player const* player, SpellInfo const* spe 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); + return IsAtInteractDistance(*player, GetInteractionDistance()); } bool GameObject::IsAtInteractDistance(Position const& pos, float radius) const @@ -2650,17 +2631,23 @@ bool GameObject::IsAtInteractDistance(Position const& pos, float radius) const 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); + QuaternionData worldRotation = GetWorldRotation(); + G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.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 G3D::CoordinateFrame{ { worldRotationQuat }, { GetPositionX(), GetPositionY(), GetPositionZ() } } + .toWorldSpace(G3D::Box{ { minX, minY, minZ }, { maxX, maxY, maxZ } }) + .contains({ pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() }); } return GetExactDist(&pos) <= radius; } +bool GameObject::IsWithinDistInMap(Player const* player) const +{ + return IsInMap(this) && InSamePhase(this) && IsAtInteractDistance(player); +} + + SpellInfo const* GameObject::GetSpellForLock(Player const* player) const { if (!player) @@ -2679,7 +2666,7 @@ SpellInfo const* GameObject::GetSpellForLock(Player const* player) const if (!lock->Type[i]) continue; - if (lock->Type[i] == LOCK_KEY_SKILL) + if (lock->Type[i] == LOCK_KEY_SPELL) if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(lock->Index[i])) return spell; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 5b844d77e4..b8dd2f77be 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -289,7 +289,10 @@ 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; + bool IsAtInteractDistance(Player const* player, SpellInfo const* spell = nullptr) const; + + bool IsWithinDistInMap(Player const* player) const; + using WorldObject::IsWithinDistInMap; SpellInfo const* GetSpellForLock(Player const* player) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 282a8d16c3..b43b6fa993 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2455,7 +2455,7 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const if (!go) return nullptr; - if (!go->IsWithinDistInMap(this, go->GetInteractionDistance())) + if (!go->IsWithinDistInMap(this)) return nullptr; return go; @@ -8707,7 +8707,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) } else { - if (lootType != LOOT_FISHINGHOLE && ((lootType != LOOT_FISHING && lootType != LOOT_FISHING_JUNK) || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this, INTERACTION_DISTANCE)) + if (lootType != LOOT_FISHINGHOLE && ((lootType != LOOT_FISHING && lootType != LOOT_FISHING_JUNK) || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this)) return true; if (lootType == LOOT_CORPSE && go->GetRespawnTime() && go->isSpawnedByDefault()) diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 8f83ddfae0..613ca0ccde 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -50,7 +50,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData) GameObject* go = player->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO - if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) + if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player))) { player->SendLootRelease(lguid); return; @@ -164,7 +164,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/) GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); // do not check distance for GO if player is the owner of it (ex. fishing bobber) - if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE)))) + if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player)))) loot = &go->loot; break; @@ -321,7 +321,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) GameObject* go = GetPlayer()->GetMap()->GetGameObject(lguid); // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO - if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) + if (!go || ((go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player))) return; loot = &go->loot; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 6d8b357494..9b8077e131 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -363,6 +363,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) recvPacket >> spellId; recvPacket >> glyphIndex; recvPacket >> castFlags; + TriggerCastFlags triggerFlag = TRIGGERED_NONE; TC_LOG_DEBUG("network", "WORLD: got cast spell packet, castCount: %u, spellId: %u, castFlags: %u, data length = %u", castCount, spellId, castFlags, (uint32)recvPacket.size()); @@ -402,12 +403,33 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) caster = _player; } + // client provided targets + SpellCastTargets targets; + targets.Read(recvPacket, caster); + HandleClientCastFlags(recvPacket, castFlags, targets); + if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->IsRaidMarker() && !caster->ToPlayer()->HasArchProject(static_cast(spellInfo->ResearchProjectId))) { - // not have spell in spellbook - recvPacket.rfinish(); // prevent spam at ignore packet - return; + bool allow = false; + + // allow casting of unknown spells for special lock cases + if (GameObject *go = targets.GetGOTarget()) + if (go->GetSpellForLock(caster->ToPlayer()) == spellInfo) + allow = true; + + // TODO: Preparation for #23204 + // allow casting of spells triggered by clientside periodic trigger auras + /* + if (caster->HasAuraTypeWithTriggerSpell(SPELL_AURA_PERIODIC_TRIGGER_SPELL_FROM_CLIENT, spellId)) + { + allow = true; + triggerFlag = TRIGGERED_FULL_MASK; + } + */ + + if (!allow) + return; } // Check possible spell cast overrides @@ -415,15 +437,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) // can't use our own spells when we're in possession of another unit, if (_player->isPossessing()) - { - recvPacket.rfinish(); // prevent spam at ignore packet return; - } - - // client provided targets - SpellCastTargets targets; - targets.Read(recvPacket, caster); - HandleClientCastFlags(recvPacket, castFlags, targets); // Client is resending autoshot cast opcode when other spell is cast during shoot rotation // Skip it to prevent "interrupt" message @@ -452,7 +466,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) spellInfo = actualSpellInfo; } - Spell* spell = new Spell(caster, spellInfo, TRIGGERED_NONE, ObjectGuid::Empty, false); + Spell* spell = new Spell(caster, spellInfo, triggerFlag, ObjectGuid::Empty, false); spell->m_cast_count = castCount; // set count of casts spell->m_glyphIndex = glyphIndex; spell->prepare(&targets); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 0213c32c58..3d16bea3a8 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -2949,7 +2949,8 @@ enum LockKeyType { LOCK_KEY_NONE = 0, LOCK_KEY_ITEM = 1, - LOCK_KEY_SKILL = 2 + LOCK_KEY_SKILL = 2, + LOCK_KEY_SPELL = 3 }; enum LockType diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 2970de391c..ea30846841 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -7619,6 +7619,11 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk } return SPELL_CAST_OK; } + case LOCK_KEY_SPELL: + if (m_spellInfo->Id == lockInfo->Index[j]) + return SPELL_CAST_OK; + reqKey = true; + break; } }