feat(Core/Gameobject): add a range check for gameobjects (#7521)

This commit is contained in:
lineagedr
2021-08-31 11:34:43 +03:00
committed by GitHub
parent 1c43e6ac6e
commit ae8a78d90a
10 changed files with 256 additions and 48 deletions

View File

@@ -24,6 +24,8 @@
#include "UpdateFieldFlags.h"
#include "World.h"
#include <G3D/Quat.h>
#include <G3D/Box.h>
#include <G3D/CoordinateFrame.h>
#ifdef ELUNA
#include "LuaEngine.h"
@@ -277,15 +279,19 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
// used switch since there should be more
case 181233: // maexxna portal effect
case 181575: // maexxna portal
SetWorldRotation(rotation);
SetLocalRotation(rotation);
break;
default:
// xinef: hackfix - but make it possible to use original WorldRotation (using special gameobject addon data)
// pussywizard: temporarily calculate WorldRotation from orientation, do so until values in db are correct
if (addon && addon->invisibilityType == INVISIBILITY_GENERAL && addon->InvisibilityValue == 0)
SetWorldRotation(rotation);
{
SetLocalRotation(rotation);
}
else
SetWorldRotationAngles(NormalizeOrientation(GetOrientation()), 0.0f, 0.0f);
{
SetLocalRotationAngles(NormalizeOrientation(GetOrientation()), 0.0f, 0.0f);
}
break;
}
@@ -879,7 +885,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool
data.posY = GetPositionY();
data.posZ = GetPositionZ();
data.orientation = GetOrientation();
data.rotation = m_worldRotation;
data.rotation = m_localRotation;
data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
data.animprogress = GetGoAnimProgress();
data.go_state = GetGoState();
@@ -905,10 +911,10 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool
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()));
@@ -2003,14 +2009,14 @@ 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_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(G3D::Quat const& rot)
void GameObject::SetLocalRotation(G3D::Quat const& rot)
{
G3D::Quat rotation;
// Temporary solution for gameobjects that have no rotation data in DB:
@@ -2020,7 +2026,7 @@ void GameObject::SetWorldRotation(G3D::Quat const& rot)
rotation = rot;
rotation.unitize();
m_worldRotation = rotation;
m_localRotation = rotation;
UpdatePackedRotation();
}
@@ -2032,9 +2038,26 @@ void GameObject::SetTransportPathRotation(float qx, float qy, float qz, float qw
SetFloatValue(GAMEOBJECT_PARENTROTATION + 3, qw);
}
void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot)
void GameObject::SetLocalRotationAngles(float z_rot, float y_rot, float x_rot)
{
SetWorldRotation(G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot)));
SetLocalRotation(G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot)));
}
G3D::Quat GameObject::GetWorldRotation() const
{
G3D::Quat localRotation = GetLocalRotation();
if (Transport* transport = GetTransport())
{
G3D::Quat 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 G3D::Quat(resultRotation.x, resultRotation.y, resultRotation.z, resultRotation.w);
}
return localRotation;
}
void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/)
@@ -2509,18 +2532,39 @@ void GameObject::SetPosition(float x, float y, float z, float o)
GetMap()->GameObjectRelocation(this, x, y, z, o);
}
float GameObject::GetInteractionDistance()
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;
}
@@ -2562,3 +2606,110 @@ GameObjectModel* GameObject::CreateModel()
{
return GameObjectModel::Create(std::make_unique<GameObjectModelOwnerImpl>(this), sWorld->GetDataPath());
}
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 (sGameObjectDisplayInfoStore.LookupEntry(GetGOInfo()->displayId))
{
return IsAtInteractDistance(*player, maxRange);
}
}
return IsAtInteractDistance(*player, GetInteractionDistance());
}
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;
G3D::Quat worldRotation = GetWorldRotation();
G3D::Quat worldRotationQuat(worldRotation.x, worldRotation.y, worldRotation.z, worldRotation.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 GetExactDist(&pos) <= radius;
}
bool GameObject::IsWithinDistInMap(Player const* player) const
{
return IsInMap(player) && InSamePhase(player) && IsAtInteractDistance(player);
}
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_SPELL)
{
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;
}