fix(Core/Movement): (#7008)

- Get zone/area IDs from vmap data in the liquid update
- Add new method Map::getFullVMapDataForPosition to get area info and liquid info in a single vmap lookup
- Adjust GetZoneId/GetAreaId on WorldObject to always return these cached fields.
- Clean up liquid state handling on Unit and Player
- Implemented getting area id from gameobject spawns.
- Removed old core related to getting movement flags dependent on environment.
- Movement flags are now processed more precisely and dynamically.

Original source: TrinityCore.

- Closes #5086
- Updates #2208.
This commit is contained in:
UltraNix
2021-08-25 12:41:20 +02:00
committed by GitHub
parent 909c3e5799
commit a8c0a2cc89
47 changed files with 1086 additions and 883 deletions

View File

@@ -8,9 +8,14 @@
#include "BoundingIntervalHierarchyWrapper.h"
#include "GameObjectModel.h"
#include "Log.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "RegularGrid.h"
#include "Timer.h"
#include "VMapFactory.h"
#include "VMapManager2.h"
#include "WorldModel.h"
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include <G3D/Vector3.h>
@@ -145,6 +150,52 @@ struct DynamicTreeIntersectionCallback
bool didHit() const { return did_hit;}
};
struct DynamicTreeAreaInfoCallback
{
DynamicTreeAreaInfoCallback(uint32 phaseMask)
: _phaseMask(phaseMask) {}
void operator()(G3D::Vector3 const& p, GameObjectModel const& obj)
{
obj.IntersectPoint(p, _areaInfo, _phaseMask);
}
VMAP::AreaInfo const& GetAreaInfo() const
{
return _areaInfo;
}
private:
uint32 _phaseMask;
VMAP::AreaInfo _areaInfo;
};
struct DynamicTreeLocationInfoCallback
{
DynamicTreeLocationInfoCallback(uint32 phaseMask)
: _phaseMask(phaseMask), _hitModel(nullptr) {}
void operator()(G3D::Vector3 const& p, GameObjectModel const& obj)
{
if (obj.GetLocationInfo(p, _locationInfo, _phaseMask))
_hitModel = &obj;
}
VMAP::LocationInfo& GetLocationInfo()
{
return _locationInfo;
}
GameObjectModel const* GetHitModel() const
{
return _hitModel;
}
private:
uint32 _phaseMask;
VMAP::LocationInfo _locationInfo;
GameObjectModel const* _hitModel;
};
bool DynamicMapTree::GetIntersectionTime(const uint32 phasemask, const G3D::Ray& ray,
const G3D::Vector3& endPos, float& maxDist) const
{
@@ -238,3 +289,41 @@ float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist,
return -G3D::finf();
}
}
bool DynamicMapTree::GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
G3D::Vector3 v(x, y, z + 0.5f);
DynamicTreeAreaInfoCallback intersectionCallBack(phasemask);
impl->intersectPoint(v, intersectionCallBack);
if (intersectionCallBack.GetAreaInfo().result)
{
flags = intersectionCallBack.GetAreaInfo().flags;
adtId = intersectionCallBack.GetAreaInfo().adtId;
rootId = intersectionCallBack.GetAreaInfo().rootId;
groupId = intersectionCallBack.GetAreaInfo().groupId;
z = intersectionCallBack.GetAreaInfo().ground_Z;
return true;
}
return false;
}
void DynamicMapTree::GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const
{
G3D::Vector3 v(x, y, z + 0.5f);
DynamicTreeLocationInfoCallback intersectionCallBack(phasemask);
impl->intersectPoint(v, intersectionCallBack);
if (intersectionCallBack.GetLocationInfo().hitModel)
{
data.floorZ = intersectionCallBack.GetLocationInfo().ground_Z;
uint32 liquidType = intersectionCallBack.GetLocationInfo().hitModel->GetLiquidType();
float liquidLevel;
if (!reqLiquidType || (dynamic_cast<VMAP::VMapManager2*>(VMAP::VMapFactory::createOrGetVMapManager())->GetLiquidFlagsPtr(liquidType) & reqLiquidType))
if (intersectionCallBack.GetHitModel()->GetLiquidLevel(v, intersectionCallBack.GetLocationInfo(), liquidLevel))
data.liquidInfo.emplace(liquidType, liquidLevel);
data.areaInfo.emplace(0,
intersectionCallBack.GetLocationInfo().rootId,
intersectionCallBack.GetLocationInfo().hitModel->GetWmoID(),
intersectionCallBack.GetLocationInfo().hitModel->GetMogpFlags());
}
}

View File

@@ -15,6 +15,11 @@ namespace G3D
class Vector3;
}
namespace VMAP
{
struct AreaAndLiquidData;
}
class GameObjectModel;
struct DynTreeImpl;
@@ -32,6 +37,9 @@ public:
bool GetIntersectionTime(uint32 phasemask, const G3D::Ray& ray,
const G3D::Vector3& endPos, float& maxDist) const;
bool GetAreaInfo(float x, float y, float& z, uint32 phasemask, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const;
void GetAreaAndLiquidData(float x, float y, float z, uint32 phasemask, uint8 reqLiquidType, VMAP::AreaAndLiquidData& data) const;
bool GetObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1,
const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos,
float pModifyDist) const;

View File

@@ -8,6 +8,7 @@
#define _IVMAPMANAGER_H
#include "Define.h"
#include "Optional.h"
#include <string>
//===========================================================
@@ -25,8 +26,33 @@ namespace VMAP
VMAP_LOAD_RESULT_IGNORED
};
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
struct AreaAndLiquidData
{
struct AreaInfo
{
AreaInfo(int32 _adtId, int32 _rootId, int32 _groupId, uint32 _flags)
: adtId(_adtId), rootId(_rootId), groupId(_groupId), mogpFlags(_flags) { }
int32 const adtId;
int32 const rootId;
int32 const groupId;
uint32 const mogpFlags;
};
struct LiquidInfo
{
LiquidInfo(uint32 _type, float _level)
: type(_type), level(_level) {}
uint32 const type;
float const level;
};
float floorZ = VMAP_INVALID_HEIGHT;
Optional<AreaInfo> areaInfo;
Optional<LiquidInfo> liquidInfo;
};
//===========================================================
class IVMapManager
@@ -79,8 +105,10 @@ namespace VMAP
Query world model area info.
\param z gets adjusted to the ground height for which this are info is valid
*/
virtual bool GetAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const = 0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float& level, float& floor, uint32& type) const = 0;
virtual bool GetAreaInfo(uint32 pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const = 0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const = 0;
// get both area + liquid data in a single vmap lookup
virtual void GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const = 0;
};
}

View File

@@ -255,7 +255,7 @@ namespace VMAP
return VMAP_INVALID_HEIGHT_VALUE;
}
bool VMapManager2::GetAreaInfo(unsigned int mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
bool VMapManager2::GetAreaInfo(uint32 mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
@@ -275,7 +275,7 @@ namespace VMAP
return false;
}
bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const
bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const
{
#if defined(ENABLE_VMAP_CHECKS)
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
@@ -291,6 +291,7 @@ namespace VMAP
floor = info.ground_Z;
ASSERT(floor < std::numeric_limits<float>::max());
type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
mogpFlags = info.hitModel->GetMogpFlags();
if (reqLiquidType && !(GetLiquidFlagsPtr(type) & reqLiquidType))
{
return false;
@@ -306,6 +307,38 @@ namespace VMAP
return false;
}
void VMapManager2::GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const
{
if (IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_LIQUIDSTATUS))
{
data.floorZ = z;
int32 adtId, rootId, groupId;
uint32 flags;
if (GetAreaInfo(mapId, x, y, data.floorZ, flags, adtId, rootId, groupId))
data.areaInfo.emplace(adtId, rootId, groupId, flags);
return;
}
InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
LocationInfo info;
Vector3 pos = convertPositionToInternalRep(x, y, z);
if (instanceTree->second->GetLocationInfo(pos, info))
{
data.floorZ = info.ground_Z;
uint32 liquidType = info.hitModel->GetLiquidType();
float liquidLevel;
if (!reqLiquidType || (GetLiquidFlagsPtr(liquidType) & reqLiquidType))
if (info.hitInstance->GetLiquidLevel(pos, info, liquidLevel))
data.liquidInfo.emplace(liquidType, liquidLevel);
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
data.areaInfo.emplace(info.hitInstance->adtId, info.rootId, info.hitModel->GetWmoID(), info.hitModel->GetMogpFlags());
}
}
}
WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename)
{
//! Critical section, thread safe access to iLoadedModelFiles

View File

@@ -117,8 +117,9 @@ namespace VMAP
bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
bool GetAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const override;
bool GetAreaInfo(uint32 pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const override;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type, uint32& mogpFlags) const override;
void GetAreaAndLiquidData(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, AreaAndLiquidData& data) const override;
WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename);
void releaseModelInstance(const std::string& filename);

View File

@@ -23,6 +23,7 @@ namespace VMAP
const ModelInstance* hitInstance{nullptr};
const GroupModel* hitModel{nullptr};
float ground_Z;
int32 rootId = -1;
};
class StaticMapTree

View File

@@ -6,6 +6,7 @@
#include "GameObjectModel.h"
#include "Log.h"
#include "MapTree.h"
#include "Timer.h"
#include "VMapDefinitions.h"
#include "VMapFactory.h"
@@ -146,6 +147,7 @@ bool GameObjectModel::initialize(std::unique_ptr<GameObjectModelOwnerBase> model
#endif
owner = std::move(modelOwner);
isWmo = it->second.isWmo;
return true;
}
@@ -187,6 +189,69 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto
return hit;
}
void GameObjectModel::IntersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, uint32 ph_mask) const
{
if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject())
return;
if (!iBound.contains(point))
return;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
info.ground_Z = world_Z;
}
}
bool GameObjectModel::GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const
{
if (!(phasemask & ph_mask) || !owner->IsSpawned() || !IsMapObject())
return false;
if (!iBound.contains(point))
return false;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
{
info.ground_Z = world_Z;
return true;
}
}
return false;
}
bool GameObjectModel::GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const
{
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (point - iPos) * iInvScale;
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (info.hitModel->GetLiquidLevel(pModel, zDist))
{
// calculate world height (zDist in model coords):
// assume WMO not tilted (wouldn't make much sense anyway)
liqHeight = zDist * iScale + iPos.z;
return true;
}
return false;
}
bool GameObjectModel::UpdatePosition()
{
if (!iModel)

View File

@@ -16,6 +16,8 @@
namespace VMAP
{
class WorldModel;
struct AreaInfo;
struct LocationInfo;
}
class GameObject;
@@ -37,7 +39,7 @@ public:
class GameObjectModel
{
GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(nullptr) { }
GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(nullptr), isWmo(false) { }
public:
std::string name;
@@ -53,8 +55,12 @@ public:
void enable(uint32 ph_mask) { phasemask = ph_mask; }
[[nodiscard]] bool isEnabled() const { return phasemask != 0; }
[[nodiscard]] bool IsMapObject() const { return isWmo; }
bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const;
void IntersectPoint(G3D::Vector3 const& point, VMAP::AreaInfo& info, uint32 ph_mask) const;
bool GetLocationInfo(G3D::Vector3 const& point, VMAP::LocationInfo& info, uint32 ph_mask) const;
bool GetLiquidLevel(G3D::Vector3 const& point, VMAP::LocationInfo& info, float& liqHeight) const;
static GameObjectModel* Create(std::unique_ptr<GameObjectModelOwnerBase> modelOwner, std::string const& dataPath);
@@ -71,6 +77,7 @@ private:
float iScale;
VMAP::WorldModel* iModel;
std::unique_ptr<GameObjectModelOwnerBase> owner;
bool isWmo;
};
void LoadGameObjectModelList(std::string const& dataPath);

View File

@@ -412,7 +412,6 @@ namespace VMAP
{
return false;
}
GModelRayCallback callback(triangles, vertices);
Vector3 rPos = pos - 0.1f * down;
float dist = G3D::inf();
G3D::Ray ray(rPos, down);
@@ -554,6 +553,7 @@ namespace VMAP
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.rootId = RootWMOID;
info.hitModel = &(*callback.hit);
dist = callback.zDist;
return true;