aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/Collision/DynamicTree.cpp13
-rw-r--r--src/common/Collision/DynamicTree.h1
-rw-r--r--src/common/Collision/Management/IVMapManager.h3
-rw-r--r--src/common/Collision/Management/VMapManager2.cpp19
-rw-r--r--src/common/Collision/Management/VMapManager2.h1
-rw-r--r--src/common/Collision/Maps/MapTree.cpp13
-rw-r--r--src/common/Collision/Maps/MapTree.h1
-rw-r--r--src/server/game/Entities/Object/Object.cpp185
-rw-r--r--src/server/game/Entities/Object/Object.h2
-rw-r--r--src/server/game/Maps/Map.cpp50
-rw-r--r--src/server/game/Maps/Map.h57
-rw-r--r--src/server/game/Spells/Spell.cpp26
-rw-r--r--src/server/game/Spells/SpellEffects.cpp1
13 files changed, 301 insertions, 71 deletions
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp
index 96378ef9c0d..a69d54c7f9a 100644
--- a/src/common/Collision/DynamicTree.cpp
+++ b/src/common/Collision/DynamicTree.cpp
@@ -254,3 +254,16 @@ float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist,
else
return -G3D::finf();
}
+
+float DynamicMapTree::getCeil(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
+{
+ G3D::Vector3 v(x, y, z);
+ G3D::Ray r(v, G3D::Vector3(0, 0, 1));
+ DynamicTreeIntersectionCallback callback(phasemask);
+ impl->intersectZAllignedRay(r, callback, maxSearchDist);
+
+ if (callback.didHit())
+ return v.z + maxSearchDist;
+
+ return G3D::finf();
+}
diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h
index 9b7d5f15e20..aa7a4bb3d2a 100644
--- a/src/common/Collision/DynamicTree.h
+++ b/src/common/Collision/DynamicTree.h
@@ -51,6 +51,7 @@ public:
float pModifyDist) const;
float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
+ float getCeil(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
void insert(GameObjectModel const&);
void remove(GameObjectModel const&);
diff --git a/src/common/Collision/Management/IVMapManager.h b/src/common/Collision/Management/IVMapManager.h
index e9aae51a025..7fa9ddb12e3 100644
--- a/src/common/Collision/Management/IVMapManager.h
+++ b/src/common/Collision/Management/IVMapManager.h
@@ -49,6 +49,8 @@ namespace VMAP
#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_CEIL_VALUE 200000.0f
+ #define VMAP_INVALID_CEIL 100000.0f
struct AreaAndLiquidData
{
@@ -92,6 +94,7 @@ namespace VMAP
virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, ModelIgnoreFlags ignoreFlags) = 0;
virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0;
+ virtual float getCeil(unsigned int /*pMapId*/, float /*x*/, float /*y*/, float /*z*/, float /*maxSearchDist*/) { return VMAP_INVALID_CEIL_VALUE; }
/**
test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found
return a position, that is pReduceDist closer to the origin
diff --git a/src/common/Collision/Management/VMapManager2.cpp b/src/common/Collision/Management/VMapManager2.cpp
index 5b9cb34874b..922d68140d3 100644
--- a/src/common/Collision/Management/VMapManager2.cpp
+++ b/src/common/Collision/Management/VMapManager2.cpp
@@ -234,6 +234,25 @@ namespace VMAP
return VMAP_INVALID_HEIGHT_VALUE;
}
+ float VMapManager2::getCeil(unsigned int mapId, float x, float y, float z, float maxSearchDist)
+ {
+ if (isHeightCalcEnabled() && !IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_HEIGHT))
+ {
+ InstanceTreeMap::const_iterator instanceTree = GetMapTree(mapId);
+ if (instanceTree != iInstanceMapTrees.end())
+ {
+ Vector3 pos = convertPositionToInternalRep(x, y, z);
+ float height = instanceTree->second->getCeil(pos, maxSearchDist);
+ if (!(height < G3D::finf()))
+ return height = VMAP_INVALID_CEIL_VALUE; // No height
+
+ return height;
+ }
+ }
+
+ return VMAP_INVALID_CEIL_VALUE;
+ }
+
bool VMapManager2::getAreaInfo(unsigned int mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
if (!IsVMAPDisabledForPtr(mapId, VMAP_DISABLE_AREAFLAG))
diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h
index 831383ac555..b50348b8928 100644
--- a/src/common/Collision/Management/VMapManager2.h
+++ b/src/common/Collision/Management/VMapManager2.h
@@ -113,6 +113,7 @@ namespace VMAP
*/
bool getObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) override;
float getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist) override;
+ float getCeil(unsigned int mapId, float x, float y, float z, float maxSearchDist) override;
bool processCommand(char* /*command*/) override { return false; } // for debug and extensions
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp
index 3a927acc310..c96adb6c87c 100644
--- a/src/common/Collision/Maps/MapTree.cpp
+++ b/src/common/Collision/Maps/MapTree.cpp
@@ -236,6 +236,19 @@ namespace VMAP
return(height);
}
+ float StaticMapTree::getCeil(const G3D::Vector3 & pPos, float maxSearchDist) const
+ {
+ float height = G3D::finf();
+ Vector3 dir = Vector3(0, 0, 1);
+ G3D::Ray ray(pPos, dir); // direction with length of 1
+ float maxDist = maxSearchDist;
+ if (getIntersectionTime(ray, maxDist, false, ModelIgnoreFlags::Nothing))
+ {
+ height = pPos.z + maxDist;
+ }
+ return(height);
+ }
+
//=========================================================
LoadResult StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
{
diff --git a/src/common/Collision/Maps/MapTree.h b/src/common/Collision/Maps/MapTree.h
index 48470bf0261..186f1120915 100644
--- a/src/common/Collision/Maps/MapTree.h
+++ b/src/common/Collision/Maps/MapTree.h
@@ -75,6 +75,7 @@ namespace VMAP
bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2, ModelIgnoreFlags ignoreFlags) const;
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
+ float getCeil(const G3D::Vector3& pPos, float maxSearchDist) const;
bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 763fdc1b4cc..bd41de9e657 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1487,7 +1487,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
else
{
float ground_z = GetMapHeight(x, y, z);
- if (z < ground_z)
+ if (std::fabs(z - ground_z) < GetCollisionHeight())
z = ground_z;
}
break;
@@ -1510,7 +1510,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
else
{
float ground_z = GetMapHeight(x, y, z);
- if (z < ground_z)
+ if (std::fabs(z - ground_z) < GetCollisionHeight())
z = ground_z;
}
break;
@@ -2305,13 +2305,95 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle)
pos.SetOrientation(GetOrientation());
}
+// Only call this function when you already checked for collision with vmap/gameobject.
+// You should check for collision again after this one has been called.
+// excludeCollisionHeight should only be true if you had collision, it wont add it to raycasts for dest position.
+float WorldObject::SelectBestZForDestination(float x, float y, float z, bool excludeCollisionHeight) const
+{
+ if (Unit const* unit = ToUnit())
+ {
+ float const ground = GetFloorZ();
+ bool const isInAir = (G3D::fuzzyGt(unit->GetPositionZMinusOffset(), ground + GROUND_HEIGHT_TOLERANCE) || G3D::fuzzyLt(unit->GetPositionZMinusOffset(), ground - GROUND_HEIGHT_TOLERANCE));
+ if (unit->IsFlying() && isInAir)
+ return z;
+ }
+
+ float myX, myY, myZ;
+ GetPosition(myX, myY, myZ);
+
+ float const myCollisionHeight = GetCollisionHeight();
+ float const destCollisionHeight = excludeCollisionHeight ? 0.0f : myCollisionHeight;
+
+ float const myGridHeight = GetMap()->GetGridMapHeight(myX, myY);
+ float const myVmapFloor = std::max(GetMap()->GetVMapFloor(myX, myY, myZ, 150.0f, myCollisionHeight),
+ GetMap()->GetGameObjectFloor(GetPhaseMask(), myX, myY, myZ, 150.0f, myCollisionHeight));
+
+ // which of these 3 do I want ?
+ float const destGridHeight = GetMap()->GetGridMapHeight(x, y);
+ float const destCeil = GetMap()->GetCeil(GetPhaseMask(), x, y, z, 150.0f, destCollisionHeight);
+ float const destVmapFloor = std::max(GetMap()->GetVMapFloor(x, y, z, 150.0f, destCollisionHeight),
+ GetMap()->GetGameObjectFloor(GetPhaseMask(), x, y, z, 150.0f, destCollisionHeight));
+
+ bool const hasVmapFloor = myVmapFloor > INVALID_HEIGHT;
+ bool const hasDestGridHeight = destGridHeight > INVALID_HEIGHT;
+ bool const hasDestVmapCeil = destCeil < VMAP_INVALID_CEIL && destCeil != destVmapFloor;
+ bool const hasDestVmapFloor = destVmapFloor > INVALID_HEIGHT;
+ bool const destBetweenVmaps = hasDestVmapCeil && hasDestVmapFloor;
+ bool const noVmap = !hasDestVmapFloor && !hasDestVmapCeil;
+
+ // It is possible that while moving, our feet are slightly moving under the ground. Jumping / reconnecting fixes this issue but we don't want to rely on that.
+ myZ += myCollisionHeight;
+ bool const isOnVmap = hasVmapFloor &&
+ ((myZ < myGridHeight && std::fabs(myVmapFloor - myZ) < std::fabs(myGridHeight - myZ)) ||
+ (myZ > myGridHeight && myVmapFloor > myGridHeight));
+
+ bool const hasToFollowGridHeight = hasDestGridHeight && (noVmap ||
+ (z > destGridHeight && destGridHeight > destVmapFloor) ||
+ (z < destGridHeight && hasDestVmapFloor && !hasDestVmapCeil) ||
+ (z < destGridHeight && !hasDestVmapFloor) ||
+ (destBetweenVmaps && !isOnVmap && destGridHeight > destVmapFloor && destGridHeight < destCeil));
+
+ float result = INVALID_HEIGHT;
+ if (hasToFollowGridHeight)
+ {
+ result = destGridHeight;
+ if (hasDestVmapFloor)
+ if (std::fabs(destVmapFloor - destGridHeight) < myCollisionHeight)
+ result = std::max(destVmapFloor, destGridHeight);
+
+ if (hasDestVmapCeil)
+ if (std::fabs(destCeil - destGridHeight) < myCollisionHeight)
+ result = std::max(destCeil, destGridHeight);
+ }
+ else if (hasDestVmapFloor)
+ result = destVmapFloor;
+ else if (hasDestVmapCeil)
+ result = destCeil;
+
+ if (Unit const* unit = ToUnit())
+ if (!unit->CanSwim())
+ return result;
+
+ LiquidData liquidData;
+ ZLiquidStatus const liquidStatus = GetMap()->GetLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &liquidData, destCollisionHeight);
+ switch (liquidStatus)
+ {
+ case LIQUID_MAP_ABOVE_WATER:
+ return std::max<float>(liquidData.level, result);
+ case LIQUID_MAP_IN_WATER:
+ case LIQUID_MAP_UNDER_WATER:
+ return std::max<float>(z, result);
+ default:
+ return result;
+ }
+}
+
void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float angle)
{
angle += GetOrientation();
- float destx, desty, destz;
- destx = pos.m_positionX + dist * std::cos(angle);
- desty = pos.m_positionY + dist * std::sin(angle);
- destz = pos.m_positionZ;
+ float destx = pos.m_positionX + dist * std::cos(angle);
+ float desty = pos.m_positionY + dist * std::sin(angle);
+ float destz = pos.m_positionZ;
// Prevent invalid coordinates here, position is unchanged
if (!Trinity::IsValidMapCoord(destx, desty))
@@ -2320,52 +2402,72 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float
return;
}
- UpdateAllowedPositionZ(destx, desty, destz);
- bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), pos.m_positionX, pos.m_positionY, pos.m_positionZ, destx, desty, destz, destx, desty, destz, -0.5f);
-
- // collision occured
+ bool const col = ComputeCollisionPosition(pos, { destx, desty, destz }, destx, desty, destz);
if (col)
- {
- // move back a bit
- destx -= CONTACT_DISTANCE * std::cos(angle);
- desty -= CONTACT_DISTANCE * std::sin(angle);
dist = std::sqrt((pos.m_positionX - destx)*(pos.m_positionX - destx) + (pos.m_positionY - desty)*(pos.m_positionY - desty));
- }
- // check dynamic collision
- col = GetMap()->getObjectHitPos(GetPhaseMask(), pos.m_positionX, pos.m_positionY, pos.m_positionZ, destx, desty, destz, destx, desty, destz, -0.5f);
+ destz = SelectBestZForDestination(destx, desty, destz, col);
- // Collided with a gameobject
- if (col)
+ float const step = dist / 10.0f;
+ // do not allow too big z changes
+ for (uint8 j = 0; j < 10 && std::fabs(pos.m_positionZ - destz) > 6.0f; ++j)
{
- destx -= CONTACT_DISTANCE * std::cos(angle);
- desty -= CONTACT_DISTANCE * std::sin(angle);
- dist = std::sqrt((pos.m_positionX - destx)*(pos.m_positionX - destx) + (pos.m_positionY - desty)*(pos.m_positionY - desty));
+ destx -= step * std::cos(angle);
+ desty -= step * std::sin(angle);
+ // There should not be any collision between our position and destx, desty, pos.m_positionZ at this point.
+ // Use pos.m_positionZ here because destz was not good.
+ destz = SelectBestZForDestination(destx, desty, pos.m_positionZ, col);
}
- float step = dist / 10.0f;
+ Trinity::NormalizeMapCoord(destx);
+ Trinity::NormalizeMapCoord(desty);
+ // We might want to loop until there is no more collision with a better z position. (And/or until a fixed #attemps have been made).
+ ComputeCollisionPosition(pos, { destx, desty, destz }, destx, desty, destz);
+ pos.Relocate(destx, desty, destz);
+ pos.SetOrientation(GetOrientation());
+}
- for (uint8 j = 0; j < 10; ++j)
+bool WorldObject::ComputeCollisionPosition(Position const& startPosition, Position const& endPosition, float& x, float& y, float& z) const
+{
+ Position vmapCollisionPos;
+ bool const vmapCollision = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(GetMapId(), startPosition.m_positionX, startPosition.m_positionY, startPosition.m_positionZ + GetCollisionHeight(), endPosition.m_positionX, endPosition.m_positionY, endPosition.m_positionZ + GetCollisionHeight(), vmapCollisionPos.m_positionX, vmapCollisionPos.m_positionY, vmapCollisionPos.m_positionZ, -CONTACT_DISTANCE * 2.0f);
+ Position gameObjectCollisionPos;
+ bool const gameObjectCollision = GetMap()->getObjectHitPos(GetPhaseMask(), startPosition.m_positionX, startPosition.m_positionY, startPosition.m_positionZ + GetCollisionHeight(), endPosition.m_positionX, endPosition.m_positionY, endPosition.m_positionZ + GetCollisionHeight(), gameObjectCollisionPos.m_positionX, gameObjectCollisionPos.m_positionY, gameObjectCollisionPos.m_positionZ, -CONTACT_DISTANCE * 2.0f);
+
+ // Both collision occures, check which one is closest to start.
+ if (vmapCollision && gameObjectCollision)
{
- // do not allow too big z changes
- if (std::fabs(pos.m_positionZ - destz) > 6.0f)
+ if (startPosition.GetExactDist(vmapCollision) < startPosition.GetExactDist(gameObjectCollisionPos))
{
- destx -= step * std::cos(angle);
- desty -= step * std::sin(angle);
- UpdateAllowedPositionZ(destx, desty, destz);
- }
- // we have correct destz now
- else
- {
- pos.Relocate(destx, desty, destz);
- break;
+ x = vmapCollisionPos.m_positionX;
+ y = vmapCollisionPos.m_positionY;
+ z = vmapCollisionPos.m_positionZ;
+ return true;
}
+
+ x = gameObjectCollisionPos.m_positionX;
+ y = gameObjectCollisionPos.m_positionY;
+ z = gameObjectCollisionPos.m_positionZ;
+ return true;
}
- Trinity::NormalizeMapCoord(pos.m_positionX);
- Trinity::NormalizeMapCoord(pos.m_positionY);
- UpdateAllowedPositionZ(destx, desty, pos.m_positionZ);
- pos.SetOrientation(GetOrientation());
+ if (vmapCollision)
+ {
+ x = vmapCollisionPos.m_positionX;
+ y = vmapCollisionPos.m_positionY;
+ z = vmapCollisionPos.m_positionZ;
+ return true;
+ }
+
+ if (gameObjectCollision)
+ {
+ x = gameObjectCollisionPos.m_positionX;
+ y = gameObjectCollisionPos.m_positionY;
+ z = gameObjectCollisionPos.m_positionZ;
+ return true;
+ }
+
+ return false;
}
void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update)
@@ -2558,10 +2660,7 @@ float WorldObject::GetMapWaterOrGroundLevel(float x, float y, float z, float* gr
float WorldObject::GetMapHeight(float x, float y, float z, bool vmap/* = true*/, float distanceToSearch/* = DEFAULT_HEIGHT_SEARCH*/) const
{
- if (z != MAX_HEIGHT)
- z += GetCollisionHeight();
-
- return GetMap()->GetHeight(GetPhaseMask(), x, y, z, vmap, distanceToSearch);
+ return GetMap()->GetHeight(GetPhaseMask(), x, y, z, vmap, distanceToSearch, GetCollisionHeight());
}
template TC_GAME_API void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>&, uint32, float) const;
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index fd4b34d7892..a3311615c4f 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -274,6 +274,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void MovePosition(Position &pos, float dist, float angle);
Position GetNearPosition(float dist, float angle);
void MovePositionToFirstCollision(Position &pos, float dist, float angle);
+ bool ComputeCollisionPosition(Position const& startPosition, Position const& endPosition, float& x, float& y, float& z) const;
Position GetFirstCollisionPosition(float dist, float angle);
Position GetRandomNearPosition(float radius);
void GetContactPoint(WorldObject const* obj, float &x, float &y, float &z, float distance2d = CONTACT_DISTANCE) const;
@@ -492,6 +493,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
bool CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkAlert = false) const;
bool CanDetectInvisibilityOf(WorldObject const* obj) const;
bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const;
+ float SelectBestZForDestination(float x, float y, float z, bool excludeCollisionHeight) const;
};
namespace Trinity
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 6958392f66d..8a55e13da77 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -2377,12 +2377,12 @@ inline GridMap* Map::GetGrid(float x, float y)
return GridMaps[gx][gy];
}
-float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground /*= nullptr*/, bool /*swim = false*/, float collisionHeight /*= DEFAULT_COLLISION_HEIGHT*/) const
+float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground /*= nullptr*/, bool /*swim = false*/, float collisionHeight /*= DEFAULT_COLLISION_HEIGHT*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
{
if (const_cast<Map*>(this)->GetGrid(x, y))
{
// we need ground level (including grid height version) for proper return water level in point
- float ground_z = GetHeight(phasemask, x, y, z + collisionHeight, true, 50.0f);
+ float ground_z = GetHeight(phasemask, x, y, z, true, maxSearchDist, collisionHeight);
if (ground)
*ground = ground_z;
@@ -2403,30 +2403,22 @@ float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, fl
return VMAP_INVALID_HEIGHT_VALUE;
}
-float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
+float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/, float collisionHeight) const
{
// find raw .map surface under Z coordinates
- float mapHeight = VMAP_INVALID_HEIGHT_VALUE;
- if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
- {
- float gridHeight = gmap->getHeight(x, y);
- if (z > gridHeight)
- mapHeight = gridHeight;
- }
+ float const gridHeight = GetGridMapHeight(x, y);
+ float mapHeight = z + collisionHeight > gridHeight ? gridHeight : VMAP_INVALID_HEIGHT_VALUE;
float vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
if (checkVMap)
- {
- VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
- if (vmgr->isHeightCalcEnabled())
- vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist);
- }
+ vmapHeight = GetVMapFloor(x, y, z, maxSearchDist, collisionHeight);
- // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
- // vmapheight set for any under Z value or <= INVALID_HEIGHT
- if (vmapHeight > INVALID_HEIGHT)
+ bool const hasVmapFloor = vmapHeight > INVALID_HEIGHT;
+ bool const hasMapFloor = mapHeight > INVALID_HEIGHT;
+
+ if (hasVmapFloor)
{
- if (mapHeight > INVALID_HEIGHT)
+ if (hasMapFloor)
{
// we have mapheight and vmapheight and must select more appropriate
@@ -2441,7 +2433,7 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
return vmapHeight; // we have only vmapHeight (if have)
}
- return mapHeight; // explicitly use map data
+ return gridHeight; // explicitly use map data
}
float Map::GetMinHeight(float x, float y) const
@@ -2452,6 +2444,19 @@ float Map::GetMinHeight(float x, float y) const
return -500.0f;
}
+float Map::GetGridMapHeight(float x, float y) const
+{
+ if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
+ return gmap->getHeight(x, y);
+
+ return VMAP_INVALID_HEIGHT_VALUE;
+}
+
+float Map::GetVMapFloor(float x, float y, float z, float maxSearchDist, float collisionHeight) const
+{
+ return VMAP::VMapFactory::createOrGetVMapManager()->getHeight(GetId(), x, y, z + collisionHeight, maxSearchDist);
+}
+
inline bool IsOutdoorWMO(uint32 mogpFlags, int32 /*adtId*/, int32 /*rootId*/, int32 /*groupId*/, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry)
{
bool outdoor = true;
@@ -2771,6 +2776,11 @@ float Map::GetWaterLevel(float x, float y) const
return 0;
}
+float Map::GetCeil(float x, float y, float z, float maxSearchDist, float collisionHeight) const
+{
+ return VMAP::VMapFactory::createOrGetVMapManager()->getCeil(GetId(), x, y, z + collisionHeight, maxSearchDist);
+}
+
bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const
{
if ((checks & LINEOFSIGHT_CHECK_VMAP)
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 37e8f3294e9..9c1f4748b06 100644
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -538,20 +538,63 @@ class TC_GAME_API Map : public GridRefManager<NGridType>
BattlegroundMap* ToBattlegroundMap() { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap*>(this); else return nullptr; }
BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; }
- float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = 2.03128f) const; // DEFAULT_COLLISION_HEIGHT in Object.h
+ // FLOOR, CEIL AND HEIGHT
+ float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = 0.0f, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
float GetMinHeight(float x, float y) const;
- float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const;
- float GetHeight(Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
- float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist)); }
- float GetHeight(uint32 phasemask, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const { return GetHeight(phasemask, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); }
+ float GetGridMapHeight(float x, float y) const;
+ float GetVMapFloor(float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const;
+ float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const;
+ float GetHeight(Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return GetHeight(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist, collisionHeight);
+ }
+
+ float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return std::max<float>(GetHeight(x, y, z, vmap, maxSearchDist, collisionHeight), GetGameObjectFloor(phasemask, x, y, z, maxSearchDist, collisionHeight));
+ }
+
+ float GetHeight(uint32 phasemask, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return GetHeight(phasemask, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist, collisionHeight);
+ }
+
+ float GetCeil(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return std::min<float>(GetCeil(x, y, z, maxSearchDist, collisionHeight), GetGameObjectCeil(phasemask, x, y, z, maxSearchDist, collisionHeight));
+ }
+
+ float GetCeil(uint32 phasemask, Position const& pos, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return GetCeil(phasemask, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), maxSearchDist, collisionHeight);
+ }
+
+ float GetCeil(Position const& pos, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return GetCeil(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), maxSearchDist, collisionHeight);
+ }
+
+ float GetCeil(float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const;
+
+ float GetGameObjectCeil(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return _dynamicTree.getCeil(x, y, z + collisionHeight, maxSearchDist, phasemask);
+ }
+
+ float GetGameObjectCeil(uint32 phasemask, Position const& pos, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
+ {
+ return GetGameObjectCeil(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ() + collisionHeight, maxSearchDist, phasemask);
+ }
+
+ //
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const;
void Balance() { _dynamicTree.balance(); }
void RemoveGameObjectModel(GameObjectModel const& model) { _dynamicTree.remove(model); }
void InsertGameObjectModel(GameObjectModel const& model) { _dynamicTree.insert(model); }
bool ContainsGameObjectModel(GameObjectModel const& model) const { return _dynamicTree.contains(model);}
- float GetGameObjectFloor(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const
+ float GetGameObjectFloor(uint32 phasemask, float x, float y, float z, float maxSearchDist = DEFAULT_HEIGHT_SEARCH, float collisionHeight = 0.0f) const
{
- return _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
+ return _dynamicTree.getHeight(x, y, z + collisionHeight, maxSearchDist, phasemask);
}
bool getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float modifyDist);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index b6b5c22b6e1..7eaad1835ec 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1359,6 +1359,32 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici
dest = SpellDestination(x, y, liquidLevel, m_caster->GetOrientation());
break;
}
+ case TARGET_DEST_CASTER_FRONT_LEAP:
+ {
+ float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
+ float angle = targetType.CalcDirectionAngle();
+
+ Position pos = dest._position;
+
+ m_caster->MovePositionToFirstCollision(pos, dist, angle);
+ // Generate path to that point.
+ if (!m_preGeneratedPath)
+ m_preGeneratedPath = std::make_unique<PathGenerator>(m_caster);
+
+ m_preGeneratedPath->SetPathLengthLimit(dist);
+
+ // Should we use straightline here ? What do we do when we don't have a full path ?
+ bool pathResult = m_preGeneratedPath->CalculatePath(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false, true);
+ if (pathResult && m_preGeneratedPath->GetPathType() & (PATHFIND_NORMAL | PATHFIND_SHORTCUT))
+ {
+ pos.m_positionX = m_preGeneratedPath->GetActualEndPosition().x;
+ pos.m_positionY = m_preGeneratedPath->GetActualEndPosition().y;
+ pos.m_positionZ = m_preGeneratedPath->GetActualEndPosition().z;
+ }
+
+ dest.Relocate(pos);
+ break;
+ }
default:
{
float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index b1691bb4d2a..9ea2928c423 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -4426,7 +4426,6 @@ void Spell::EffectLeap(SpellEffIndex /*effIndex*/)
return;
Position pos = destTarget->GetPosition();
- pos = unitTarget->GetFirstCollisionPosition(unitTarget->GetDistance(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()), 0.0f);
unitTarget->NearTeleportTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), unitTarget == m_caster);
}