From 918b0b46b10702fde10a15e96ab2df70d1fb8435 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Tue, 23 Sep 2025 15:15:09 +0200 Subject: [PATCH 01/27] =?UTF-8?q?fix(Core/Map/Grids):=20Sphere=E2=80=93Pla?= =?UTF-8?q?ne=20intersection=20implementation=20(accurate=20ground=20heigh?= =?UTF-8?q?t=20calculation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/Entities/Object/Object.cpp | 26 ++++ src/server/game/Entities/Object/Object.h | 2 + src/server/game/Grids/GridTerrainData.cpp | 145 +++++++++++++++++++++ src/server/game/Grids/GridTerrainData.h | 3 + src/server/game/Maps/Map.cpp | 58 +++++++++ src/server/game/Maps/Map.h | 3 + src/server/scripts/Commands/cs_misc.cpp | 5 + 7 files changed, 242 insertions(+) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a183d14672..973a63fe4d 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3017,6 +3017,32 @@ float WorldObject::GetMapHeight(float x, float y, float z, bool vmap/* = true*/, return GetMap()->GetHeight(GetPhaseMask(), x, y, z, vmap, distanceToSearch); } +float WorldObject::GetGroundProbeRadius() const +{ + if (Unit const* u = ToUnit()) + return std::max(0.25f, u->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS)); + return 0.3f; +} + +float WorldObject::GetMapHeightAccurate(float x, float y, float z, bool vmap/* = true*/, + float distanceToSearch/* = DEFAULT_HEIGHT_SEARCH*/, float radius/* = 0.3f*/) const +{ + if (radius <= 0.0f) + { + if (Unit const* u = ToUnit()) + { + radius = std::max(0.25f, u->GetFloatValue(UNIT_FIELD_BOUNDINGRADIUS)); + } + else + radius = 0.3f; + } + + if (z != MAX_HEIGHT) + z += std::max(GetCollisionHeight(), radius + 0.2f); + + return GetMap()->GetHeightAccurate(GetPhaseMask(), x, y, z, radius, vmap, distanceToSearch); +} + float WorldObject::GetMapWaterOrGroundLevel(float x, float y, float z, float* ground/* = nullptr*/) const { return GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), x, y, z, ground, diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 23784c9c63..94f8dbe220 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -703,6 +703,8 @@ public: }; [[nodiscard]] float GetMapWaterOrGroundLevel(float x, float y, float z, float* ground = nullptr) const; [[nodiscard]] float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h + [[nodiscard]] float GetGroundProbeRadius() const; + [[nodiscard]] float GetMapHeightAccurate(float x, float y, float z, bool vmap = true, float distanceToSearch = DEFAULT_HEIGHT_SEARCH, float radius = 0.3f) const; [[nodiscard]] float GetFloorZ() const; [[nodiscard]] float GetMinHeightInWater() const; diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index e2439dc2e5..de5886c893 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -615,3 +615,148 @@ LiquidData const GridTerrainData::GetLiquidData(float x, float y, float z, float return liquidData; } + +static inline float CELL_SIZE() { return SIZE_OF_GRIDS / float(MAP_RESOLUTION); } // ≈ 4.1666667f + +bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const +{ + if (!_loadedHeightData) + return false; + + // FLOAT + if (_loadedHeightData->floatHeightData) + { + auto const& v9 = _loadedHeightData->floatHeightData->v9; + auto const& v8 = _loadedHeightData->floatHeightData->v8; + + h1 = v9[xInt * 129 + yInt]; + h2 = v9[(xInt + 1) * 129 + yInt]; + h3 = v9[xInt * 129 + yInt + 1]; + h4 = v9[(xInt + 1) * 129 + yInt + 1]; + h5 = v8[xInt * 128 + yInt]; + return true; + } + + // UINT8 + if (_loadedHeightData->uint8HeightData) + { + auto const& d = *_loadedHeightData->uint8HeightData; + float k = d.gridIntHeightMultiplier; + float base = _loadedHeightData->gridHeight; + + auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt + h1 = float(v9ptr[0]) * k + base; + h2 = float(v9ptr[129]) * k + base; + h3 = float(v9ptr[1]) * k + base; + h4 = float(v9ptr[130]) * k + base; + + uint8 v8val = d.v8[xInt * 128 + yInt]; + h5 = float(v8val) * k + base; + return true; + } + + // UINT16 + if (_loadedHeightData->uint16HeightData) + { + auto const& d = *_loadedHeightData->uint16HeightData; + float k = d.gridIntHeightMultiplier; + float base = _loadedHeightData->gridHeight; + + auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt + h1 = float(v9ptr[0]) * k + base; + h2 = float(v9ptr[129]) * k + base; + h3 = float(v9ptr[1]) * k + base; + h4 = float(v9ptr[130]) * k + base; + + uint16 v8val = d.v8[xInt * 128 + yInt]; + h5 = float(v8val) * k + base; + return true; + } + + return false; +} + +float GridTerrainData::GetHeightAccurate(float x, float y, float radius) const +{ + if (!_loadedHeightData) + return INVALID_HEIGHT; + + float xf = MAP_RESOLUTION * (32.0f - x / SIZE_OF_GRIDS); + float yf = MAP_RESOLUTION * (32.0f - y / SIZE_OF_GRIDS); + + int xInt = int(xf); + int yInt = int(yf); + float fx = xf - xInt; + float fy = yf - yInt; + xInt &= (MAP_RESOLUTION - 1); + yInt &= (MAP_RESOLUTION - 1); + + if (isHole(xInt, yInt)) + return INVALID_HEIGHT; + + float h1, h2, h3, h4, h5; + if (!SampleHeights(xInt, yInt, h1, h2, h3, h4, h5)) + return INVALID_HEIGHT; + + // h1 -> (0,0) + // h2 -> (S,0) + // h3 -> (0,S) + // h4 -> (S,S) + // h5 -> (S/2, S/2) + const float S = CELL_SIZE(); + const float S2 = S * 0.5f; + + G3D::Vector3 P(fx * S, fy * S, 0.0f); + G3D::Vector3 A(S2, S2, h5); + G3D::Vector3 B, C; + + if (fx + fy < 1.0f) + { + if (fx > fy) + { + B = G3D::Vector3(S, 0.0f, h2); + C = G3D::Vector3(0.0f, 0.0f, h1); + } + else + { + B = G3D::Vector3(0.0f, 0.0f, h1); + C = G3D::Vector3(0.0f, S, h3); + } + } + else + { + if (fx > fy) + { + B = G3D::Vector3(S, S, h4); + C = G3D::Vector3(S, 0, h2); + } + else + { + B = G3D::Vector3(0, S, h3); + C = G3D::Vector3(S, S, h4); + } + } + + G3D::Vector3 U = B - A; + G3D::Vector3 V = C - A; + G3D::Vector3 n = U.cross(V); + + float nz = n.z; + float const eps = 1e-6f; + + if (std::abs(nz) < eps) + return getHeight(x, y); + + if (nz < 0.0f) + n = -n; + + n = n.unit(); + + float d = -n.dot(A); + float z = (radius - d - n.x * P.x - n.y * P.y) / n.z; + + if (!std::isfinite(z)) + return getHeight(x, y); + + return z; +} diff --git a/src/server/game/Grids/GridTerrainData.h b/src/server/game/Grids/GridTerrainData.h index 6d6b898252..36582414aa 100644 --- a/src/server/game/Grids/GridTerrainData.h +++ b/src/server/game/Grids/GridTerrainData.h @@ -245,11 +245,14 @@ public: ~GridTerrainData() { }; TerrainMapDataReadResult Load(std::string const& mapFileName); + float GetHeightAccurate(float x, float y, float radius) const; uint16 getArea(float x, float y) const; inline float getHeight(float x, float y) const { return (this->*_gridGetHeight)(x, y); } float getMinHeight(float x, float y) const; float getLiquidLevel(float x, float y) const; LiquidData const GetLiquidData(float x, float y, float z, float collisionHeight, uint8 ReqLiquidType) const; +private: + bool SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const; }; #endif diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index d8dde935e5..782834fdb1 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1175,6 +1175,47 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float return mapHeight; // explicitly use map data } +float Map::GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const +{ + // find raw .map surface under Z coordinates + float mapHeight = VMAP_INVALID_HEIGHT_VALUE; + float gridHeight = GetGridHeightAccurate(x, y, radius); + if (gridHeight > INVALID_HEIGHT) + { + const float tol = std::max(0.1f, 0.5f * radius); // tolerancia dinámica en función del tamaño del que pisa + if (G3D::fuzzyGe(z, gridHeight - tol)) + mapHeight = gridHeight; + } + + float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + if (checkVMap) + { + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // VMAP: mismo criterio que GetHeight + } + + // 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) + { + if (mapHeight > INVALID_HEIGHT) + { + // we have mapheight and vmapheight and must select more appropriate + + // we are already under the surface or vmap height above map heigt + // or if the distance of the vmap height is less the land height distance + if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z)) + return vmapHeight; + else + return mapHeight; // better use .map surface height + } + else + return vmapHeight; // we have only vmapHeight (if have) + } + + return mapHeight; // explicitly use map data +} + float Map::GetGridHeight(float x, float y) const { if (GridTerrainData* gmap = const_cast(this)->GetGridTerrainData(x, y)) @@ -1183,6 +1224,14 @@ float Map::GetGridHeight(float x, float y) const return INVALID_HEIGHT; } +float Map::GetGridHeightAccurate(float x, float y, float radius) const +{ + if (GridTerrainData* gmap = const_cast(this)->GetGridTerrainData(x, y)) + return gmap->GetHeightAccurate(x, y, radius); + + return INVALID_HEIGHT; +} + float Map::GetMinHeight(float x, float y) const { if (GridTerrainData const* grid = const_cast(this)->GetGridTerrainData(x, y)) @@ -1598,6 +1647,15 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr return std::max(h1, h2); } +float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, + bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const +{ + float h1, h2; + h1 = GetHeightAccurate(x, y, z, radius, vmap, maxSearchDist); + h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + return std::max(h1, h2); +} + bool Map::IsInWater(uint32 phaseMask, float x, float y, float pZ, float collisionHeight) const { LiquidData const& liquidData = const_cast(this)->GetLiquidData(phaseMask, x, y, pZ, collisionHeight, MAP_ALL_LIQUIDS); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index c875e4eaa9..4def40910f 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -235,8 +235,10 @@ public: // some calls like isInWater should not use vmaps due to processor power // can return INVALID_HEIGHT if under z+2 z coord not found height + [[nodiscard]] float GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; [[nodiscard]] float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; [[nodiscard]] float GetGridHeight(float x, float y) const; + [[nodiscard]] float GetGridHeightAccurate(float x, float y, float radius) const; [[nodiscard]] float GetMinHeight(float x, float y) const; Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = nullptr); @@ -379,6 +381,7 @@ public: float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = DEFAULT_COLLISION_HEIGHT) const; [[nodiscard]] float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + [[nodiscard]] float GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; [[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, PathGenerator *path, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 41546214ce..c0e34ebf1d 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -628,6 +628,8 @@ public: float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); + float GridZAccurate = map->GetGridHeightAccurate(x, y, object->GetGroundProbeRadius()); + float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, object->GetGroundProbeRadius()); uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; uint32 haveVMap = GridTerrainLoader::ExistVMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; @@ -658,6 +660,9 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP); + handler->PSendSysMessage("Accurate Height Grid: %f", GridZAccurate); + handler->PSendSysMessage("Accurate Height Map: %f", MapZAccurate); + LiquidData const& liquidData = object->GetLiquidData(); if (liquidData.Status) From 8cf85f6b8d66fc71dfd1ae84e837669cb0d71da7 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Tue, 23 Sep 2025 16:10:11 +0200 Subject: [PATCH 02/27] fix Trailing whitespaces --- src/server/game/Maps/Map.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 782834fdb1..fcff715f78 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1176,14 +1176,14 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float } float Map::GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const -{ +{ // find raw .map surface under Z coordinates float mapHeight = VMAP_INVALID_HEIGHT_VALUE; float gridHeight = GetGridHeightAccurate(x, y, radius); if (gridHeight > INVALID_HEIGHT) { const float tol = std::max(0.1f, 0.5f * radius); // tolerancia dinámica en función del tamaño del que pisa - if (G3D::fuzzyGe(z, gridHeight - tol)) + if (G3D::fuzzyGe(z, gridHeight - tol)) mapHeight = gridHeight; } @@ -1649,7 +1649,7 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const -{ +{ float h1, h2; h1 = GetHeightAccurate(x, y, z, radius, vmap, maxSearchDist); h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); From bb18ac07df4f4a3a501d153d5bbe5389d38b37b7 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Tue, 23 Sep 2025 16:18:49 +0200 Subject: [PATCH 03/27] English comments --- src/server/game/Maps/Map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index fcff715f78..78f2f5c2fe 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1182,7 +1182,7 @@ float Map::GetHeightAccurate(float x, float y, float z, float radius, bool check float gridHeight = GetGridHeightAccurate(x, y, radius); if (gridHeight > INVALID_HEIGHT) { - const float tol = std::max(0.1f, 0.5f * radius); // tolerancia dinámica en función del tamaño del que pisa + const float tol = std::max(0.1f, 0.5f * radius); // dynamic tolerance based on the size of the collider if (G3D::fuzzyGe(z, gridHeight - tol)) mapHeight = gridHeight; } @@ -1191,7 +1191,7 @@ float Map::GetHeightAccurate(float x, float y, float z, float radius, bool check if (checkVMap) { VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); - vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // VMAP: mismo criterio que GetHeight + vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // VMAP: same criterion as GetHeight } // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT From f0e28838dc60d8d037c0584e516f0d9d3bfb7082 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Tue, 23 Sep 2025 16:21:44 +0200 Subject: [PATCH 04/27] Manager to Mgr --- src/server/game/Maps/Map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 78f2f5c2fe..a2f5e2063e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1190,7 +1190,7 @@ float Map::GetHeightAccurate(float x, float y, float z, float radius, bool check float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; if (checkVMap) { - VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // VMAP: same criterion as GetHeight } From f3bd5adf8e9a670eb088cab35c7e8297cf5ca055 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Tue, 23 Sep 2025 16:28:16 +0200 Subject: [PATCH 05/27] . --- src/server/scripts/Commands/cs_misc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index c0e34ebf1d..4bdbf991f7 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -621,6 +621,7 @@ public: AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId); AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); + Map* map = object->GetMap(); float zoneX = object->GetPositionX(); float zoneY = object->GetPositionY(); @@ -628,7 +629,7 @@ public: float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); - float GridZAccurate = map->GetGridHeightAccurate(x, y, object->GetGroundProbeRadius()); + float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetGroundProbeRadius()); float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, object->GetGroundProbeRadius()); uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; From 795570ba8fb63d44741d8381e4f191f3b328ea3e Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 10:17:16 +0200 Subject: [PATCH 06/27] Square vs Cylinder --- src/server/game/Entities/Object/Object.cpp | 8 +- src/server/game/Grids/GridTerrainData.cpp | 95 ++++++++++++---------- src/server/game/Grids/GridTerrainData.h | 9 ++ src/server/game/Maps/Map.cpp | 34 ++++++-- src/server/game/Maps/Map.h | 9 +- src/server/game/World/WorldConfig.cpp | 7 ++ src/server/game/World/WorldConfig.h | 5 +- src/server/scripts/Commands/cs_misc.cpp | 17 +++- 8 files changed, 129 insertions(+), 55 deletions(-) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 973a63fe4d..e465b5d1ba 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3037,10 +3037,16 @@ float WorldObject::GetMapHeightAccurate(float x, float y, float z, bool vmap/* = radius = 0.3f; } + float rScale = 1.0f; + if (sWorld) + rScale = std::max(0.1f, sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE)); + radius *= rScale; + + float yaw = GetOrientation(); if (z != MAX_HEIGHT) z += std::max(GetCollisionHeight(), radius + 0.2f); - return GetMap()->GetHeightAccurate(GetPhaseMask(), x, y, z, radius, vmap, distanceToSearch); + return GetMap()->GetHeightAccurate(GetPhaseMask(), x, y, z, radius, yaw, vmap, distanceToSearch); } float WorldObject::GetMapWaterOrGroundLevel(float x, float y, float z, float* ground/* = nullptr*/) const diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index de5886c893..1595b363b9 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -3,6 +3,7 @@ #include "GridTerrainData.h" #include "Log.h" #include "MapDefines.h" +#include "World.h" #include #include @@ -616,6 +617,11 @@ LiquidData const GridTerrainData::GetLiquidData(float x, float y, float z, float return liquidData; } +namespace +{ + constexpr float INV_SQRT2 = 0.70710678118654752440f; // 1/sqrt(2) +} + static inline float CELL_SIZE() { return SIZE_OF_GRIDS / float(MAP_RESOLUTION); } // ≈ 4.1666667f bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const @@ -677,6 +683,16 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& } float GridTerrainData::GetHeightAccurate(float x, float y, float radius) const +{ + return GetHeightAccurate(x, y, radius, GroundFootprintShape::Circle, 0.0f); +} + +float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const +{ + return GetHeightAccurate(x, y, radius, shape, 0.0f); +} + +float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw) const { if (!_loadedHeightData) return INVALID_HEIGHT; @@ -707,56 +723,53 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius) const const float S2 = S * 0.5f; G3D::Vector3 P(fx * S, fy * S, 0.0f); + G3D::Vector3 A(S2, S2, h5); - G3D::Vector3 B, C; - if (fx + fy < 1.0f) + const float eps = 1e-6f; + auto evalTri = [&](G3D::Vector3 const& B, G3D::Vector3 const& C) -> float { - if (fx > fy) + G3D::Vector3 U = B - A; + G3D::Vector3 V = C - A; + G3D::Vector3 n = U.cross(V); + const float nzAbs = std::abs(n.z); + if (nzAbs < eps) + return -1.0e30f; // inválido + + const float d = -(n.x * A.x + n.y * A.y + n.z * A.z); + const float zPlane = (-d - n.x * P.x - n.y * P.y) / n.z; + const float denom = std::max(eps, nzAbs); + + if (shape == GroundFootprintShape::Square) { - B = G3D::Vector3(S, 0.0f, h2); - C = G3D::Vector3(0.0f, 0.0f, h1); + float blend = 0.0f; + if (sWorld) + blend = std::clamp(sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND), 0.0f, 1.0f); + float c = std::cos(yaw), s = std::sin(yaw); + float rx = n.x * c + n.y * s; + float ry = -n.x * s + n.y * c; + const float slopeL1 = (std::abs(rx) + std::abs(ry)) / denom; + const float slopeL2 = std::sqrt(std::max(0.0f, n.x * n.x + n.y * n.y)) / denom; + const float addSq = (radius * (1.0f - blend) * INV_SQRT2) * slopeL1; + const float addCir = (radius * blend) * slopeL2; + return zPlane + addSq + addCir; } else { - B = G3D::Vector3(0.0f, 0.0f, h1); - C = G3D::Vector3(0.0f, S, h3); + const float slopeL2 = std::sqrt(std::max(0.0f, n.x * n.x + n.y * n.y)) / denom; + return zPlane + radius * slopeL2; } - } - else - { - if (fx > fy) - { - B = G3D::Vector3(S, S, h4); - C = G3D::Vector3(S, 0, h2); - } - else - { - B = G3D::Vector3(0, S, h3); - C = G3D::Vector3(S, S, h4); - } - } + }; - G3D::Vector3 U = B - A; - G3D::Vector3 V = C - A; - G3D::Vector3 n = U.cross(V); + float bestZ = -1.0e30f; + bool have = false; + auto take = [&](float zc) { if (std::isfinite(zc)) { have = true; if (zc > bestZ) bestZ = zc; } }; + take(evalTri(G3D::Vector3(S, 0.0f, h2), G3D::Vector3(0.0f, 0.0f, h1))); // center-h2-h1 + take(evalTri(G3D::Vector3(S, S, h4), G3D::Vector3(S, 0.0f, h2))); // center-h4-h2 + take(evalTri(G3D::Vector3(0.0f, S, h3), G3D::Vector3(S, S, h4))); // center-h3-h4 + take(evalTri(G3D::Vector3(0.0f, 0.0f,h1), G3D::Vector3(0.0f, S, h3))); // center-h1-h3 - float nz = n.z; - float const eps = 1e-6f; - - if (std::abs(nz) < eps) + if (!have) return getHeight(x, y); - - if (nz < 0.0f) - n = -n; - - n = n.unit(); - - float d = -n.dot(A); - float z = (radius - d - n.x * P.x - n.y * P.y) / n.z; - - if (!std::isfinite(z)) - return getHeight(x, y); - - return z; + return bestZ; } diff --git a/src/server/game/Grids/GridTerrainData.h b/src/server/game/Grids/GridTerrainData.h index 36582414aa..b68513cedd 100644 --- a/src/server/game/Grids/GridTerrainData.h +++ b/src/server/game/Grids/GridTerrainData.h @@ -245,7 +245,16 @@ public: ~GridTerrainData() { }; TerrainMapDataReadResult Load(std::string const& mapFileName); + enum class GroundFootprintShape : uint8 + { + Circle = 0, // cylinder-plane + Square = 1 // upside-down pyramid + }; + float GetHeightAccurate(float x, float y, float radius) const; + float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const; + + float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw /*rads*/) const; uint16 getArea(float x, float y) const; inline float getHeight(float x, float y) const { return (this->*_gridGetHeight)(x, y); } float getMinHeight(float x, float y) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index a2f5e2063e..779af9a78e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1176,10 +1176,15 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float } float Map::GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const +{ + return GetHeightAccurate(x, y, z, radius, 0.0f, checkVMap, maxSearchDist); +} + +float Map::GetHeightAccurate(float x, float y, float z, float radius, float yaw, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { // find raw .map surface under Z coordinates float mapHeight = VMAP_INVALID_HEIGHT_VALUE; - float gridHeight = GetGridHeightAccurate(x, y, radius); + float gridHeight = GetGridHeightAccurate(x, y, radius, yaw); if (gridHeight > INVALID_HEIGHT) { const float tol = std::max(0.1f, 0.5f * radius); // dynamic tolerance based on the size of the collider @@ -1224,14 +1229,24 @@ float Map::GetGridHeight(float x, float y) const return INVALID_HEIGHT; } -float Map::GetGridHeightAccurate(float x, float y, float radius) const +float Map::GetGridHeightAccurate(float x, float y, float radius, float yaw) const { if (GridTerrainData* gmap = const_cast(this)->GetGridTerrainData(x, y)) - return gmap->GetHeightAccurate(x, y, radius); + { + GridTerrainData::GroundFootprintShape shape = GridTerrainData::GroundFootprintShape::Circle; + if (sWorld && sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) + shape = GridTerrainData::GroundFootprintShape::Square; + return gmap->GetHeightAccurate(x, y, radius, shape, yaw); + } return INVALID_HEIGHT; } +float Map::GetGridHeightAccurate(float x, float y, float radius) const +{ + return GetGridHeightAccurate(x, y, radius, 0.0f); +} + float Map::GetMinHeight(float x, float y) const { if (GridTerrainData const* grid = const_cast(this)->GetGridTerrainData(x, y)) @@ -1650,10 +1665,15 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { - float h1, h2; - h1 = GetHeightAccurate(x, y, z, radius, vmap, maxSearchDist); - h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); - return std::max(h1, h2); + return GetHeightAccurate(phasemask, x, y, z, radius, 0.0f, vmap, maxSearchDist); +} + +float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, float yaw, + bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const +{ + const float hMapMix = GetHeightAccurate(x, y, z, radius, yaw, vmap, maxSearchDist); + const float hDyn = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + return std::max(hMapMix, hDyn); } bool Map::IsInWater(uint32 phaseMask, float x, float y, float pZ, float collisionHeight) const diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 4def40910f..9222e19f52 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -235,10 +235,12 @@ public: // some calls like isInWater should not use vmaps due to processor power // can return INVALID_HEIGHT if under z+2 z coord not found height - [[nodiscard]] float GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + [[nodiscard]] float GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; // wrapper yaw=0 + [[nodiscard]] float GetHeightAccurate(float x, float y, float z, float radius, float yaw, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; [[nodiscard]] float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; [[nodiscard]] float GetGridHeight(float x, float y) const; - [[nodiscard]] float GetGridHeightAccurate(float x, float y, float radius) const; + [[nodiscard]] float GetGridHeightAccurate(float x, float y, float radius) const; // wrapper yaw=0 + [[nodiscard]] float GetGridHeightAccurate(float x, float y, float radius, float yaw) const; [[nodiscard]] float GetMinHeight(float x, float y) const; Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = nullptr); @@ -381,7 +383,8 @@ public: float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = DEFAULT_COLLISION_HEIGHT) const; [[nodiscard]] float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; - [[nodiscard]] float GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + [[nodiscard]] float GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; // wrapper yaw=0 + [[nodiscard]] float GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, float yaw, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; [[nodiscard]] bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, PathGenerator *path, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; bool CanReachPositionAndGetValidCoords(WorldObject const* source, float &destX, float &destY, float &destZ, bool failOnCollision = true, bool failOnSlopes = true) const; diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index afcede3af6..7a6b78c959 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -282,6 +282,13 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false); SetConfigValue(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_ENABLE, "Height.Accurate.Enable", true); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 1); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f), 0.05f, 4.0f); + float raw = sConfigMgr->GetOption("Height.Accurate.SquareBlend", 0.20f); + float clamped = std::clamp(raw, 0.0f, 1.0f); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", clamped); + SetConfigValue(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1); SetConfigValue(CONFIG_OBJECT_SPARKLES, "Visibility.ObjectSparkles", true); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 2760faad1d..1a00a90a3d 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -480,7 +480,10 @@ enum ServerConfigs RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, CONFIG_NEW_CHAR_STRING, CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, - + CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, + CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, + CONFIG_HEIGHT_ACCURATE_ENABLE, + CONFIG_HEIGHT_ACCURATE_SHAPE, MAX_NUM_SERVER_CONFIGS }; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 4bdbf991f7..e0bada31dd 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -629,8 +629,12 @@ public: float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); - float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetGroundProbeRadius()); - float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, object->GetGroundProbeRadius()); + float probeR = object->GetGroundProbeRadius(); + float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeR); + float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, probeR); + const char* shapeStr = "circle"; + if (sWorld && sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) + shapeStr = "square"; uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; uint32 haveVMap = GridTerrainLoader::ExistVMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; @@ -661,8 +665,17 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP); + float rScale = 1.0f; + float blend = 0.0f; + if (sWorld) + { + rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); + blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + } + handler->PSendSysMessage("Accurate Height Shape: %s (SquareBlend: %.2f, RadiusScale: %.2f)", shapeStr, blend, rScale); handler->PSendSysMessage("Accurate Height Grid: %f", GridZAccurate); handler->PSendSysMessage("Accurate Height Map: %f", MapZAccurate); + handler->PSendSysMessage("Probe radius (pre-scale): %.3f", probeR); LiquidData const& liquidData = object->GetLiquidData(); From 4f1fcb10fde656d3992c2f298aefc32efb10aaea Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 10:29:47 +0200 Subject: [PATCH 07/27] Configs --- .../apps/worldserver/worldserver.conf.dist | 23 +++++++++++++++++++ src/server/game/World/WorldConfig.cpp | 1 - src/server/game/World/WorldConfig.h | 1 - 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index fa4baa1382..6612b92eba 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1389,6 +1389,29 @@ vmap.BlizzlikeLOSInOpenWorld = 1 vmap.enableIndoorCheck = 1 +# +# Accurate ground settlement (cylinder–plane) settings +# 0 = cylinder-circle footprint (recommended), +# 1 = square footprint (upside-down-pyramid) +# + +Height.Accurate.Shape = 1 + +# +# Scale factor applied to the probe radius (safety margin, do not abuse) +# + +Height.Accurate.RadiusScale = 1.0 + +# +# Square and cylinder mix [0..1] +# 0.00 = pure square +# 1.00 = cylinder (circle) (equal to Shape=0) +# Recommended for QA: 0.15–0.25 +# + +Height.Accurate.SquareBlend = 0.2 + # # DetectPosCollision # Description: Check final move position, summon position, etc for visible collision with diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 7a6b78c959..a6cfbd9ffb 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -282,7 +282,6 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false); SetConfigValue(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_ENABLE, "Height.Accurate.Enable", true); SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 1); SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f), 0.05f, 4.0f); float raw = sConfigMgr->GetOption("Height.Accurate.SquareBlend", 0.20f); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 1a00a90a3d..580ec8ada4 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -482,7 +482,6 @@ enum ServerConfigs CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, - CONFIG_HEIGHT_ACCURATE_ENABLE, CONFIG_HEIGHT_ACCURATE_SHAPE, MAX_NUM_SERVER_CONFIGS }; From 66c87d231ac00777a1fc14a018d70b188cd5f7c7 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 10:32:48 +0200 Subject: [PATCH 08/27] . --- src/server/game/Maps/Map.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 779af9a78e..dd7a4531f3 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1671,8 +1671,8 @@ float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, float yaw, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { - const float hMapMix = GetHeightAccurate(x, y, z, radius, yaw, vmap, maxSearchDist); - const float hDyn = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + const float hMapMix = GetHeightAccurate(x, y, z, radius, yaw, vmap, maxSearchDist); + const float hDyn = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); return std::max(hMapMix, hDyn); } From 5684cd63d4c46aec8c192fde381b87e741c2f943 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 10:38:18 +0200 Subject: [PATCH 09/27] . --- src/server/scripts/Commands/cs_misc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index e0bada31dd..5dc87cd59f 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -672,10 +672,10 @@ public: rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); } - handler->PSendSysMessage("Accurate Height Shape: %s (SquareBlend: %.2f, RadiusScale: %.2f)", shapeStr, blend, rScale); - handler->PSendSysMessage("Accurate Height Grid: %f", GridZAccurate); - handler->PSendSysMessage("Accurate Height Map: %f", MapZAccurate); - handler->PSendSysMessage("Probe radius (pre-scale): %.3f", probeR); + handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f})", shapeStr, blend, rScale); + handler->PSendSysMessage("Accurate Height Grid: {}", GridZAccurate); + handler->PSendSysMessage("Accurate Height Map: {}", MapZAccurate); + handler->PSendSysMessage("Probe radius (pre-scale): {:0.3f}", probeR); LiquidData const& liquidData = object->GetLiquidData(); From c4afc5534eb434e5701b5ef84f5ccf3997165928 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 10:45:33 +0200 Subject: [PATCH 10/27] . --- src/server/apps/worldserver/worldserver.conf.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 6612b92eba..012c32e9f8 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1391,7 +1391,7 @@ vmap.enableIndoorCheck = 1 # # Accurate ground settlement (cylinder–plane) settings -# 0 = cylinder-circle footprint (recommended), +# 0 = cylinder-circle footprint (recommended), # 1 = square footprint (upside-down-pyramid) # From 0b9b84389ca54d0863b78b47e9a427dbb73c5eab Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 11:02:19 +0200 Subject: [PATCH 11/27] . --- src/server/game/World/WorldConfig.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index a6cfbd9ffb..f79ab2c919 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -283,10 +283,12 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f); SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 1); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f), 0.05f, 4.0f); - float raw = sConfigMgr->GetOption("Height.Accurate.SquareBlend", 0.20f); - float clamped = std::clamp(raw, 0.0f, 1.0f); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", clamped); + float rawSc = sConfigMgr->GetOption("Height.Accurate.RadiusScale", 1.0f); + float clampedSc = std::clamp(rawSc, 0.05f, 4.0f); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", clampedSc); + float rawSq = sConfigMgr->GetOption("Height.Accurate.SquareBlend", 0.20f); + float clampedSq = std::clamp(rawSq, 0.0f, 1.0f); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", clampedSq); SetConfigValue(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1); From 9f86676a2c60150f70a325c11836d32a09b345b7 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 11:45:25 +0200 Subject: [PATCH 12/27] Default to cylinder --- src/server/apps/worldserver/worldserver.conf.dist | 2 +- src/server/game/Grids/GridTerrainData.cpp | 2 +- src/server/game/World/WorldConfig.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 012c32e9f8..3e292e5126 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1395,7 +1395,7 @@ vmap.enableIndoorCheck = 1 # 1 = square footprint (upside-down-pyramid) # -Height.Accurate.Shape = 1 +Height.Accurate.Shape = 0 # # Scale factor applied to the probe radius (safety margin, do not abuse) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 1595b363b9..cb8ccab1c0 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -734,7 +734,7 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF G3D::Vector3 n = U.cross(V); const float nzAbs = std::abs(n.z); if (nzAbs < eps) - return -1.0e30f; // inválido + return -1.0e30f; // invalid const float d = -(n.x * A.x + n.y * A.y + n.z * A.z); const float zPlane = (-d - n.x * P.x - n.y * P.y) / n.z; diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index f79ab2c919..abb777780a 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -282,7 +282,7 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false); SetConfigValue(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 1); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 0); float rawSc = sConfigMgr->GetOption("Height.Accurate.RadiusScale", 1.0f); float clampedSc = std::clamp(rawSc, 0.05f, 4.0f); SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", clampedSc); From c817ba903c2628748e4a5e5bc3bb501a1e457878 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 11:55:35 +0200 Subject: [PATCH 13/27] vs --- src/server/scripts/Commands/cs_misc.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 5dc87cd59f..b658f53c21 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -630,8 +630,14 @@ public: float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); float probeR = object->GetGroundProbeRadius(); - float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeR); - float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, probeR); + float rScale = 1.0f; + if (sWorld) + rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); + + float yaw = object->GetOrientation(); + float probeRScaled = probeR * rScale; + float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeRScaled, yaw); + float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), /*vmap=*/true, DEFAULT_HEIGHT_SEARCH, probeR); const char* shapeStr = "circle"; if (sWorld && sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) shapeStr = "square"; From 7b3546753c7e334d8a4d53bb4baae363a6581004 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 12:02:21 +0200 Subject: [PATCH 14/27] . --- src/server/scripts/Commands/cs_misc.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index b658f53c21..dfca2d369c 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -631,8 +631,12 @@ public: float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); float probeR = object->GetGroundProbeRadius(); float rScale = 1.0f; + float blend = 0.0f; if (sWorld) + { rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); + blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + } float yaw = object->GetOrientation(); float probeRScaled = probeR * rScale; @@ -671,13 +675,6 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP); - float rScale = 1.0f; - float blend = 0.0f; - if (sWorld) - { - rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); - blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); - } handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f})", shapeStr, blend, rScale); handler->PSendSysMessage("Accurate Height Grid: {}", GridZAccurate); handler->PSendSysMessage("Accurate Height Map: {}", MapZAccurate); From 2a77ab785caa16a015e3a3cd6ffe1a1e01fdccc7 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 13:24:38 +0200 Subject: [PATCH 15/27] Apply logical suggestions --- src/server/game/Entities/Object/Object.cpp | 4 +--- src/server/game/Grids/GridTerrainData.cpp | 4 +--- src/server/game/Maps/Map.cpp | 4 +--- src/server/game/World/WorldConfig.cpp | 10 +++------- src/server/scripts/Commands/cs_misc.cpp | 12 +++--------- 5 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index e465b5d1ba..a3b9e74f5b 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3037,9 +3037,7 @@ float WorldObject::GetMapHeightAccurate(float x, float y, float z, bool vmap/* = radius = 0.3f; } - float rScale = 1.0f; - if (sWorld) - rScale = std::max(0.1f, sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE)); + float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); radius *= rScale; float yaw = GetOrientation(); diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index cb8ccab1c0..ea36f7fa43 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -742,9 +742,7 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF if (shape == GroundFootprintShape::Square) { - float blend = 0.0f; - if (sWorld) - blend = std::clamp(sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND), 0.0f, 1.0f); + float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); float c = std::cos(yaw), s = std::sin(yaw); float rx = n.x * c + n.y * s; float ry = -n.x * s + n.y * c; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index dd7a4531f3..7233527bae 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1233,9 +1233,7 @@ float Map::GetGridHeightAccurate(float x, float y, float radius, float yaw) cons { if (GridTerrainData* gmap = const_cast(this)->GetGridTerrainData(x, y)) { - GridTerrainData::GroundFootprintShape shape = GridTerrainData::GroundFootprintShape::Circle; - if (sWorld && sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) - shape = GridTerrainData::GroundFootprintShape::Square; + GridTerrainData::GroundFootprintShape shape = (GroundFootprintShapes)World->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE); return gmap->GetHeightAccurate(x, y, radius, shape, yaw); } diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index abb777780a..a5f37ec102 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -282,13 +282,9 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false); SetConfigValue(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50.0f); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 0); - float rawSc = sConfigMgr->GetOption("Height.Accurate.RadiusScale", 1.0f); - float clampedSc = std::clamp(rawSc, 0.05f, 4.0f); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", clampedSc); - float rawSq = sConfigMgr->GetOption("Height.Accurate.SquareBlend", 0.20f); - float clampedSq = std::clamp(rawSq, 0.0f, 1.0f); - SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", clampedSq); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.1f && value <= 4.0f; }, ">= 0.1 and <= 4.0"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", 0.20f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 1.0f; }, ">= 0.0 and <= 1.0"); SetConfigValue(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index dfca2d369c..8459db3825 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -630,20 +630,14 @@ public: float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); float probeR = object->GetGroundProbeRadius(); - float rScale = 1.0f; - float blend = 0.0f; - if (sWorld) - { - rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); - blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); - } - + float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); + float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); float yaw = object->GetOrientation(); float probeRScaled = probeR * rScale; float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeRScaled, yaw); float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), /*vmap=*/true, DEFAULT_HEIGHT_SEARCH, probeR); const char* shapeStr = "circle"; - if (sWorld && sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) + if (sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) shapeStr = "square"; uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; From 48f966df048ef51e314a526983817626aeff45e9 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 14:07:22 +0200 Subject: [PATCH 16/27] Suggestions, fixes and improvements --- src/server/game/Entities/Object/Object.h | 2 +- src/server/game/Grids/GridTerrainData.cpp | 36 ++++++++++++++--------- src/server/game/Grids/GridTerrainData.h | 11 +++++++ src/server/game/Maps/Map.cpp | 2 +- src/server/scripts/Commands/cs_misc.cpp | 19 ++++++------ 5 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 94f8dbe220..be1a46317e 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -704,7 +704,7 @@ public: [[nodiscard]] float GetMapWaterOrGroundLevel(float x, float y, float z, float* ground = nullptr) const; [[nodiscard]] float GetMapHeight(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f) const; // DEFAULT_HEIGHT_SEARCH in map.h [[nodiscard]] float GetGroundProbeRadius() const; - [[nodiscard]] float GetMapHeightAccurate(float x, float y, float z, bool vmap = true, float distanceToSearch = DEFAULT_HEIGHT_SEARCH, float radius = 0.3f) const; + [[nodiscard]] float GetMapHeightAccurate(float x, float y, float z, bool vmap = true, float distanceToSearch = 50.0f, float radius = 0.3f) const; [[nodiscard]] float GetFloorZ() const; [[nodiscard]] float GetMinHeightInWater() const; diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index ea36f7fa43..a3b1ce2e8e 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -623,6 +623,8 @@ namespace } static inline float CELL_SIZE() { return SIZE_OF_GRIDS / float(MAP_RESOLUTION); } // ≈ 4.1666667f +static inline size_t idx129(uint32 x, uint32 y) { return size_t(y) * 129u + x; } +static inline size_t idx128(uint32 x, uint32 y) { return size_t(y) * 128u + x; } bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const { @@ -635,11 +637,11 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& auto const& v9 = _loadedHeightData->floatHeightData->v9; auto const& v8 = _loadedHeightData->floatHeightData->v8; - h1 = v9[xInt * 129 + yInt]; - h2 = v9[(xInt + 1) * 129 + yInt]; - h3 = v9[xInt * 129 + yInt + 1]; - h4 = v9[(xInt + 1) * 129 + yInt + 1]; - h5 = v8[xInt * 128 + yInt]; + h1 = v9[idx129(xInt, yInt )]; + h2 = v9[idx129(xInt + 1, yInt )]; + h3 = v9[idx129(xInt, yInt + 1)]; + h4 = v9[idx129(xInt + 1, yInt + 1)]; + h5 = v8[idx128(xInt, yInt )]; return true; } @@ -650,13 +652,13 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& float k = d.gridIntHeightMultiplier; float base = _loadedHeightData->gridHeight; - auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt + auto v9ptr = &d.v9[idx129(xInt, yInt)]; // contiguous row-major h1 = float(v9ptr[0]) * k + base; h2 = float(v9ptr[129]) * k + base; h3 = float(v9ptr[1]) * k + base; h4 = float(v9ptr[130]) * k + base; - uint8 v8val = d.v8[xInt * 128 + yInt]; + uint8 v8val = d.v8[idx128(xInt, yInt)]; h5 = float(v8val) * k + base; return true; } @@ -668,13 +670,13 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& float k = d.gridIntHeightMultiplier; float base = _loadedHeightData->gridHeight; - auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt + auto v9ptr = &d.v9[idx129(xInt, yInt)]; // contiguous row-major h1 = float(v9ptr[0]) * k + base; h2 = float(v9ptr[129]) * k + base; h3 = float(v9ptr[1]) * k + base; h4 = float(v9ptr[130]) * k + base; - uint16 v8val = d.v8[xInt * 128 + yInt]; + uint16 v8val = d.v8[idx128(xInt, yInt)]; h5 = float(v8val) * k + base; return true; } @@ -727,6 +729,8 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF G3D::Vector3 A(S2, S2, h5); const float eps = 1e-6f; + const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + auto evalTri = [&](G3D::Vector3 const& B, G3D::Vector3 const& C) -> float { G3D::Vector3 U = B - A; @@ -734,34 +738,38 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF G3D::Vector3 n = U.cross(V); const float nzAbs = std::abs(n.z); if (nzAbs < eps) - return -1.0e30f; // invalid + return std::numeric_limits::quiet_NaN(); const float d = -(n.x * A.x + n.y * A.y + n.z * A.z); const float zPlane = (-d - n.x * P.x - n.y * P.y) / n.z; const float denom = std::max(eps, nzAbs); + const float slopeL2 = std::sqrt(std::max(0.0f, n.x * n.x + n.y * n.y)) / denom; if (shape == GroundFootprintShape::Square) { - float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); float c = std::cos(yaw), s = std::sin(yaw); float rx = n.x * c + n.y * s; float ry = -n.x * s + n.y * c; const float slopeL1 = (std::abs(rx) + std::abs(ry)) / denom; - const float slopeL2 = std::sqrt(std::max(0.0f, n.x * n.x + n.y * n.y)) / denom; const float addSq = (radius * (1.0f - blend) * INV_SQRT2) * slopeL1; const float addCir = (radius * blend) * slopeL2; return zPlane + addSq + addCir; } else { - const float slopeL2 = std::sqrt(std::max(0.0f, n.x * n.x + n.y * n.y)) / denom; return zPlane + radius * slopeL2; } }; float bestZ = -1.0e30f; bool have = false; - auto take = [&](float zc) { if (std::isfinite(zc)) { have = true; if (zc > bestZ) bestZ = zc; } }; + auto take = [&](float zc) { + if (std::isfinite(zc)) { + have = true; + if (zc > bestZ) bestZ = zc; + } + }; + take(evalTri(G3D::Vector3(S, 0.0f, h2), G3D::Vector3(0.0f, 0.0f, h1))); // center-h2-h1 take(evalTri(G3D::Vector3(S, S, h4), G3D::Vector3(S, 0.0f, h2))); // center-h4-h2 take(evalTri(G3D::Vector3(0.0f, S, h3), G3D::Vector3(S, S, h4))); // center-h3-h4 diff --git a/src/server/game/Grids/GridTerrainData.h b/src/server/game/Grids/GridTerrainData.h index b68513cedd..a0f5ad9dae 100644 --- a/src/server/game/Grids/GridTerrainData.h +++ b/src/server/game/Grids/GridTerrainData.h @@ -251,10 +251,21 @@ public: Square = 1 // upside-down pyramid }; + static inline const char* ToString(GroundFootprintShape s) + { + switch (s) + { + case GroundFootprintShape::Circle: return "circle"; + case GroundFootprintShape::Square: return "square"; + } + return "unknown"; + } + float GetHeightAccurate(float x, float y, float radius) const; float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const; float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw /*rads*/) const; + inline std::string to_string(GridTerrainData::GroundFootprintShape s) { return GridTerrainData::ToString(s); } uint16 getArea(float x, float y) const; inline float getHeight(float x, float y) const { return (this->*_gridGetHeight)(x, y); } float getMinHeight(float x, float y) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 7233527bae..47aa2aa371 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1233,7 +1233,7 @@ float Map::GetGridHeightAccurate(float x, float y, float radius, float yaw) cons { if (GridTerrainData* gmap = const_cast(this)->GetGridTerrainData(x, y)) { - GridTerrainData::GroundFootprintShape shape = (GroundFootprintShapes)World->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE); + auto shape = static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)); return gmap->GetHeightAccurate(x, y, radius, shape, yaw); } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 8459db3825..0ffd49687d 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -629,16 +629,15 @@ public: float groundZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), MAX_HEIGHT); float floorZ = object->GetMapHeight(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); - float probeR = object->GetGroundProbeRadius(); - float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); - float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); - float yaw = object->GetOrientation(); - float probeRScaled = probeR * rScale; - float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeRScaled, yaw); - float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), /*vmap=*/true, DEFAULT_HEIGHT_SEARCH, probeR); - const char* shapeStr = "circle"; - if (sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE) == 1) - shapeStr = "square"; + const float probeR = object->GetGroundProbeRadius(); // pre-scale + const float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); + const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + const float yaw = object->GetOrientation(); + const float probeRScaled = probeR * rScale; + + const float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeRScaled, yaw); + const float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), /*vmap=*/true, DEFAULT_HEIGHT_SEARCH, probeR); + const char* shapeStr = GridTerrainData::ToString(static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE))); uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; uint32 haveVMap = GridTerrainLoader::ExistVMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; From f4b436f67ef02f015c5f3e660639ce98ed2a7ded Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 14:13:29 +0200 Subject: [PATCH 17/27] . --- src/server/game/Grids/GridTerrainData.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index a3b1ce2e8e..10369ee1b7 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -763,12 +763,7 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF float bestZ = -1.0e30f; bool have = false; - auto take = [&](float zc) { - if (std::isfinite(zc)) { - have = true; - if (zc > bestZ) bestZ = zc; - } - }; + auto take = [&](float zc) { if (std::isfinite(zc)) { have = true; if (zc > bestZ) bestZ = zc; } }; take(evalTri(G3D::Vector3(S, 0.0f, h2), G3D::Vector3(0.0f, 0.0f, h1))); // center-h2-h1 take(evalTri(G3D::Vector3(S, S, h4), G3D::Vector3(S, 0.0f, h2))); // center-h4-h2 From 7c58590b02a3467926940d29a442ac10d631e189 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 19:38:20 +0200 Subject: [PATCH 18/27] . --- src/server/game/Grids/GridTerrainData.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 10369ee1b7..95dc6aa044 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -623,8 +623,6 @@ namespace } static inline float CELL_SIZE() { return SIZE_OF_GRIDS / float(MAP_RESOLUTION); } // ≈ 4.1666667f -static inline size_t idx129(uint32 x, uint32 y) { return size_t(y) * 129u + x; } -static inline size_t idx128(uint32 x, uint32 y) { return size_t(y) * 128u + x; } bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& h2, float& h3, float& h4, float& h5) const { @@ -637,11 +635,11 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& auto const& v9 = _loadedHeightData->floatHeightData->v9; auto const& v8 = _loadedHeightData->floatHeightData->v8; - h1 = v9[idx129(xInt, yInt )]; - h2 = v9[idx129(xInt + 1, yInt )]; - h3 = v9[idx129(xInt, yInt + 1)]; - h4 = v9[idx129(xInt + 1, yInt + 1)]; - h5 = v8[idx128(xInt, yInt )]; + h1 = v9[xInt * 129 + yInt]; + h2 = v9[(xInt + 1) * 129 + yInt]; + h3 = v9[xInt * 129 + yInt + 1]; + h4 = v9[(xInt + 1) * 129 + yInt + 1]; + h5 = v8[xInt * 128 + yInt]; return true; } @@ -652,13 +650,13 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& float k = d.gridIntHeightMultiplier; float base = _loadedHeightData->gridHeight; - auto v9ptr = &d.v9[idx129(xInt, yInt)]; // contiguous row-major + auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt h1 = float(v9ptr[0]) * k + base; h2 = float(v9ptr[129]) * k + base; h3 = float(v9ptr[1]) * k + base; h4 = float(v9ptr[130]) * k + base; - uint8 v8val = d.v8[idx128(xInt, yInt)]; + uint8 v8val = d.v8[xInt * 128 + yInt]; h5 = float(v8val) * k + base; return true; } @@ -670,13 +668,13 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& float k = d.gridIntHeightMultiplier; float base = _loadedHeightData->gridHeight; - auto v9ptr = &d.v9[idx129(xInt, yInt)]; // contiguous row-major + auto v9ptr = &d.v9[xInt * 128 + xInt + yInt]; // == xInt*129 + yInt h1 = float(v9ptr[0]) * k + base; h2 = float(v9ptr[129]) * k + base; h3 = float(v9ptr[1]) * k + base; h4 = float(v9ptr[130]) * k + base; - uint16 v8val = d.v8[idx128(xInt, yInt)]; + uint16 v8val = d.v8[xInt * 128 + yInt]; h5 = float(v8val) * k + base; return true; } From fdd27f12c6d13b91880dd8f1f64538f48a09445b Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 21:27:37 +0200 Subject: [PATCH 19/27] . --- .../apps/worldserver/worldserver.conf.dist | 8 ++ src/server/game/Grids/GridTerrainData.cpp | 114 ++++++++++-------- src/server/game/Grids/GridTerrainData.h | 1 + src/server/game/Maps/Map.cpp | 6 +- src/server/game/World/WorldConfig.cpp | 1 + src/server/game/World/WorldConfig.h | 1 + src/server/scripts/Commands/cs_misc.cpp | 5 +- 7 files changed, 85 insertions(+), 51 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index b864a02106..6f6c91ce70 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1412,6 +1412,14 @@ Height.Accurate.RadiusScale = 1.0 Height.Accurate.SquareBlend = 0.2 +# +# Optional slope clamp (rise/run). 0.0 disables clamping. +# This limits the gradient magnitude used by both circle and square footprints, +# preventing extreme lifts on near-vertical faces. Safe upper bound: ~5.0 (≈78.7°). +# + +Height.Accurate.SlopeClamp = 0.0 + # # DetectPosCollision # Description: Check final move position, summon position, etc for visible collision with diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 95dc6aa044..493837e629 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -684,15 +684,21 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& float GridTerrainData::GetHeightAccurate(float x, float y, float radius) const { - return GetHeightAccurate(x, y, radius, GroundFootprintShape::Circle, 0.0f); + return GetHeightAccurate(x, y, radius, GroundFootprintShape::Circle, 0.0f, 1.0f, 0.0f); } float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const { - return GetHeightAccurate(x, y, radius, shape, 0.0f); + return GetHeightAccurate(x, y, radius, shape, 0.0f, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f); } float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw) const +{ + return GetHeightAccurate(x, y, radius, shape, yaw, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f); +} + +float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw, + float squareBlend, float slopeClamp) const { if (!_loadedHeightData) return INVALID_HEIGHT; @@ -700,10 +706,14 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF float xf = MAP_RESOLUTION * (32.0f - x / SIZE_OF_GRIDS); float yf = MAP_RESOLUTION * (32.0f - y / SIZE_OF_GRIDS); - int xInt = int(xf); - int yInt = int(yf); - float fx = xf - xInt; - float fy = yf - yInt; + int xInt = static_cast(std::floor(xf)); + int yInt = static_cast(std::floor(yf)); + float fx = xf - static_cast(xInt); + float fy = yf - static_cast(yInt); + if (fx < 0.0f) { fx += 1.0f; --xInt; } + if (fy < 0.0f) { fy += 1.0f; --yInt; } + if (fx >= 1.0f) { fx -= 1.0f; ++xInt; } + if (fy >= 1.0f) { fy -= 1.0f; ++yInt; } xInt &= (MAP_RESOLUTION - 1); yInt &= (MAP_RESOLUTION - 1); @@ -727,48 +737,58 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF G3D::Vector3 A(S2, S2, h5); const float eps = 1e-6f; - const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + const float blend = std::max(0.0f, std::min(1.0f, squareBlend)); - auto evalTri = [&](G3D::Vector3 const& B, G3D::Vector3 const& C) -> float - { - G3D::Vector3 U = B - A; - G3D::Vector3 V = C - A; - G3D::Vector3 n = U.cross(V); - const float nzAbs = std::abs(n.z); - if (nzAbs < eps) - return std::numeric_limits::quiet_NaN(); + const bool right = (P.x >= A.x); + const bool top = (P.y >= A.y); + G3D::Vector3 B, C; + if ( right && !top) { B = G3D::Vector3(S, 0.0f, h2); C = G3D::Vector3(0.0f, 0.0f, h1); } // BR + else if ( right && top) { B = G3D::Vector3(S, S, h4); C = G3D::Vector3(S, 0.0f, h2); } // TR + else if (!right && top) { B = G3D::Vector3(0.0f, S, h3); C = G3D::Vector3(S, S, h4); } // TL + else /* !right && !top */{ B = G3D::Vector3(0.0f, 0.0f, h1); C = G3D::Vector3(0.0f, S, h3); } // BL - const float d = -(n.x * A.x + n.y * A.y + n.z * A.z); - const float zPlane = (-d - n.x * P.x - n.y * P.y) / n.z; - const float denom = std::max(eps, nzAbs); - const float slopeL2 = std::sqrt(std::max(0.0f, n.x * n.x + n.y * n.y)) / denom; - - if (shape == GroundFootprintShape::Square) - { - float c = std::cos(yaw), s = std::sin(yaw); - float rx = n.x * c + n.y * s; - float ry = -n.x * s + n.y * c; - const float slopeL1 = (std::abs(rx) + std::abs(ry)) / denom; - const float addSq = (radius * (1.0f - blend) * INV_SQRT2) * slopeL1; - const float addCir = (radius * blend) * slopeL2; - return zPlane + addSq + addCir; - } - else - { - return zPlane + radius * slopeL2; - } - }; - - float bestZ = -1.0e30f; - bool have = false; - auto take = [&](float zc) { if (std::isfinite(zc)) { have = true; if (zc > bestZ) bestZ = zc; } }; - - take(evalTri(G3D::Vector3(S, 0.0f, h2), G3D::Vector3(0.0f, 0.0f, h1))); // center-h2-h1 - take(evalTri(G3D::Vector3(S, S, h4), G3D::Vector3(S, 0.0f, h2))); // center-h4-h2 - take(evalTri(G3D::Vector3(0.0f, S, h3), G3D::Vector3(S, S, h4))); // center-h3-h4 - take(evalTri(G3D::Vector3(0.0f, 0.0f,h1), G3D::Vector3(0.0f, S, h3))); // center-h1-h3 - - if (!have) + const G3D::Vector3 U = B - A; + const G3D::Vector3 V = C - A; + const G3D::Vector3 n = U.cross(V); + const float nzAbs = std::abs(n.z); + if (nzAbs < eps) return getHeight(x, y); - return bestZ; + + const float zPlane = A.z - (n.x * (P.x - A.x) + n.y * (P.y - A.y)) / n.z; + const float inv2S = 1.0f / (2.0f * S); + + float a = ((h2 + h4) - (h1 + h3)) * inv2S; // dz/dx + float b = ((h3 + h4) - (h1 + h2)) * inv2S; // dz/dy + + if (slopeClamp > 0.0f) { + const float g2 = a * a + b * b; + const float c2 = slopeClamp * slopeClamp; + if (g2 > c2) { + const float scale = slopeClamp / std::sqrt(g2); + a *= scale; b *= scale; + } + } + + const float slopeL2 = std::sqrt(std::max(0.0f, a*a + b*b)); + + if (radius <= 0.0f) + return zPlane; + + float totalSlope = slopeL2; + if (shape == GroundFootprintShape::Square && blend < 1.0f) { + float slopeL1; + if (a == 0.0f && b == 0.0f) { + slopeL1 = 0.0f; + } else { + const float c = std::cos(yaw); + const float s = std::sin(yaw); + const float rx = a * c + b * s; + const float ry = -a * s + b * c; + slopeL1 = std::abs(rx) + std::abs(ry); + } + totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1); + } + + // Altura final (Minkowski): base + radio * pendiente + return zPlane + radius * totalSlope; } diff --git a/src/server/game/Grids/GridTerrainData.h b/src/server/game/Grids/GridTerrainData.h index a0f5ad9dae..14571a70ab 100644 --- a/src/server/game/Grids/GridTerrainData.h +++ b/src/server/game/Grids/GridTerrainData.h @@ -261,6 +261,7 @@ public: return "unknown"; } + float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw, float squareBlend, float slopeClamp) const; float GetHeightAccurate(float x, float y, float radius) const; float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 47aa2aa371..790cefe5ac 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1233,8 +1233,10 @@ float Map::GetGridHeightAccurate(float x, float y, float radius, float yaw) cons { if (GridTerrainData* gmap = const_cast(this)->GetGridTerrainData(x, y)) { - auto shape = static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)); - return gmap->GetHeightAccurate(x, y, radius, shape, yaw); + const auto shape = static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)); + const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); + return gmap->GetHeightAccurate(x, y, radius, shape, yaw, blend, clamp); } return INVALID_HEIGHT; diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 47716a3ea1..5a49dc091c 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -285,6 +285,7 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_HEIGHT_ACCURATE_SHAPE, "Height.Accurate.Shape", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1"); SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.1f && value <= 4.0f; }, ">= 0.1 and <= 4.0"); SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", 0.20f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 1.0f; }, ">= 0.0 and <= 1.0"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP, "Height.Accurate.SlopeClamp", 0.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 10.0f; }, ">= 0.0 and <= 10.0"); SetConfigValue(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index be73fcaeb6..563b724294 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -483,6 +483,7 @@ enum ServerConfigs CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS, CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, + CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP, CONFIG_HEIGHT_ACCURATE_SHAPE, MAX_NUM_SERVER_CONFIGS }; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 0ffd49687d..00751ad093 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -632,11 +632,12 @@ public: const float probeR = object->GetGroundProbeRadius(); // pre-scale const float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); const float yaw = object->GetOrientation(); const float probeRScaled = probeR * rScale; const float GridZAccurate = map->GetGridHeightAccurate(object->GetPositionX(), object->GetPositionY(), probeRScaled, yaw); - const float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), /*vmap=*/true, DEFAULT_HEIGHT_SEARCH, probeR); + const float MapZAccurate = object->GetMapHeightAccurate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), true, DEFAULT_HEIGHT_SEARCH, probeR); const char* shapeStr = GridTerrainData::ToString(static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE))); uint32 haveMap = GridTerrainLoader::ExistMap(object->GetMapId(), cell.GridX(), cell.GridY()) ? 1 : 0; @@ -668,7 +669,7 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP); - handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f})", shapeStr, blend, rScale); + handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f}, SlopeClamp: {:0.2f})", shapeStr, blend, rScale); handler->PSendSysMessage("Accurate Height Grid: {}", GridZAccurate); handler->PSendSysMessage("Accurate Height Map: {}", MapZAccurate); handler->PSendSysMessage("Probe radius (pre-scale): {:0.3f}", probeR); From f5ce3628763d9815c2dfac52a8c6aab70b7e5b23 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 21:45:08 +0200 Subject: [PATCH 20/27] . --- src/server/game/Grids/GridTerrainData.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 493837e629..5b3cc2708c 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -760,10 +760,12 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF float a = ((h2 + h4) - (h1 + h3)) * inv2S; // dz/dx float b = ((h3 + h4) - (h1 + h2)) * inv2S; // dz/dy - if (slopeClamp > 0.0f) { + if (slopeClamp > 0.0f) + { const float g2 = a * a + b * b; const float c2 = slopeClamp * slopeClamp; - if (g2 > c2) { + if (g2 > c2) + { const float scale = slopeClamp / std::sqrt(g2); a *= scale; b *= scale; } @@ -775,11 +777,15 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF return zPlane; float totalSlope = slopeL2; - if (shape == GroundFootprintShape::Square && blend < 1.0f) { + if (shape == GroundFootprintShape::Square && blend < 1.0f) + { float slopeL1; - if (a == 0.0f && b == 0.0f) { + if (a == 0.0f && b == 0.0f) + { slopeL1 = 0.0f; - } else { + } + else + { const float c = std::cos(yaw); const float s = std::sin(yaw); const float rx = a * c + b * s; From 18ca9b9dd759a32a80da9ba9386d467e870a6595 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 21:50:17 +0200 Subject: [PATCH 21/27] . --- src/server/game/Grids/GridTerrainData.cpp | 24 +++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 5b3cc2708c..ff678c3e54 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -742,10 +742,26 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF const bool right = (P.x >= A.x); const bool top = (P.y >= A.y); G3D::Vector3 B, C; - if ( right && !top) { B = G3D::Vector3(S, 0.0f, h2); C = G3D::Vector3(0.0f, 0.0f, h1); } // BR - else if ( right && top) { B = G3D::Vector3(S, S, h4); C = G3D::Vector3(S, 0.0f, h2); } // TR - else if (!right && top) { B = G3D::Vector3(0.0f, S, h3); C = G3D::Vector3(S, S, h4); } // TL - else /* !right && !top */{ B = G3D::Vector3(0.0f, 0.0f, h1); C = G3D::Vector3(0.0f, S, h3); } // BL + if (right && !top) + { + B = G3D::Vector3(S, 0.0f, h2); + C = G3D::Vector3(0.0f, 0.0f, h1); + } // BR + else if (right && top) + { + B = G3D::Vector3(S, S, h4); + C = G3D::Vector3(S, 0.0f, h2); + } // TR + else if (!right && top) + { + B = G3D::Vector3(0.0f, S, h3); + C = G3D::Vector3(S, S, h4); + } // TL + else /* !right && !top */ + { + B = G3D::Vector3(0.0f, 0.0f, h1); + C = G3D::Vector3(0.0f, S, h3); + } // BL const G3D::Vector3 U = B - A; const G3D::Vector3 V = C - A; From 3b98fbeb94ddfebc3a35f1c01836104edad4609e Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 21:58:54 +0200 Subject: [PATCH 22/27] . --- src/server/game/Grids/GridTerrainData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index ff678c3e54..55ad7678d6 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -752,7 +752,7 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF B = G3D::Vector3(S, S, h4); C = G3D::Vector3(S, 0.0f, h2); } // TR - else if (!right && top) + else if (!right && top) { B = G3D::Vector3(0.0f, S, h3); C = G3D::Vector3(S, S, h4); From ad8cd929fd133dce5a7dce980e86478c660028f9 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Wed, 24 Sep 2025 22:00:57 +0200 Subject: [PATCH 23/27] . --- src/server/game/Grids/GridTerrainData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 55ad7678d6..6e571885a2 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -811,6 +811,6 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1); } - // Altura final (Minkowski): base + radio * pendiente + // Final height (Minkowski): base + radius * slope return zPlane + radius * totalSlope; } From a31b747bfbdbbe60df4e0c3ceb8cf95cb952e800 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Thu, 25 Sep 2025 09:02:36 +0200 Subject: [PATCH 24/27] . --- src/server/scripts/Commands/cs_misc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 00751ad093..24ae53a8ac 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -669,7 +669,7 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP); - handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f}, SlopeClamp: {:0.2f})", shapeStr, blend, rScale); + handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f}, SlopeClamp: {:0.2f})", shapeStr, blend, rScale, clamp); handler->PSendSysMessage("Accurate Height Grid: {}", GridZAccurate); handler->PSendSysMessage("Accurate Height Map: {}", MapZAccurate); handler->PSendSysMessage("Probe radius (pre-scale): {:0.3f}", probeR); From e07481c099009125c32baa51194746dbafd5203a Mon Sep 17 00:00:00 2001 From: VG-prog Date: Thu, 25 Sep 2025 17:26:54 +0200 Subject: [PATCH 25/27] vmap and dyn + improvements --- src/common/Collision/DynamicTree.cpp | 70 ++++++++++++++ src/common/Collision/DynamicTree.h | 2 + .../apps/worldserver/worldserver.conf.dist | 42 +++++++++ src/server/game/Grids/GridTerrainData.cpp | 55 ++++++----- src/server/game/Grids/GridTerrainData.h | 6 +- src/server/game/Maps/Map.cpp | 94 ++++++++++++++++++- src/server/game/Maps/Map.h | 3 + src/server/game/World/WorldConfig.cpp | 6 ++ src/server/game/World/WorldConfig.h | 6 ++ src/server/scripts/Commands/cs_misc.cpp | 7 +- 10 files changed, 262 insertions(+), 29 deletions(-) diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp index 4fcfc55b49..14952df3d7 100644 --- a/src/common/Collision/DynamicTree.cpp +++ b/src/common/Collision/DynamicTree.cpp @@ -308,6 +308,76 @@ float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, } } +static inline bool _finiteF(float v) { return std::isfinite(v); } +namespace { constexpr float INV_SQRT2 = 0.70710678118654752440f; } + +float DynamicMapTree::getHeightAccurate(float x, float y, float z, float maxSearchDist, uint32 phasemask, + float radius, float yaw, float blend, float clamp, float sampleDelta) const +{ + auto sample = [&](float sx, float sy) -> float + { + G3D::Vector3 v(sx, sy, z); + G3D::Ray r(v, G3D::Vector3(0, 0, -1)); + float dist = maxSearchDist; + DynamicTreeIntersectionCallback cb(phasemask, VMAP::ModelIgnoreFlags::Nothing); + impl->intersectZAllignedRay(r, cb, dist); + if (cb.didHit()) + return z - dist; + return std::numeric_limits::quiet_NaN(); + }; + + const float h0 = sample(x, y); + if (!_finiteF(h0)) + return -G3D::finf(); + + if (radius <= 0.0f) + return h0; + + const float d = (sampleDelta > 0.0f) ? sampleDelta + : std::max(0.05f, std::min(0.5f, radius * 0.5f)); + + const float hx1 = sample(x + d, y); + const float hx2 = sample(x - d, y); + const float hy1 = sample(x, y + d); + const float hy2 = sample(x, y - d); + + auto diff = [&](float p, float m) -> float + { + if (_finiteF(p) && _finiteF(m)) return (p - m) / (2.0f * d); + if (_finiteF(p)) return (p - h0) / d; + if (_finiteF(m)) return (h0 - m) / d; + return 0.0f; + }; + + float gx = diff(hx1, hx2); // dz/dx + float gy = diff(hy1, hy2); // dz/dy + + if (clamp > 0.0f) + { + const float g2 = gx*gx + gy*gy; + const float c2 = clamp*clamp; + if (g2 > c2) + { + const float s = clamp / std::sqrt(g2); + gx *= s; gy *= s; + } + } + + const float slopeL2 = std::sqrt(std::max(0.0f, gx*gx + gy*gy)); + float totalSlope = slopeL2; + + if (blend < 1.0f) + { + float c = std::cos(yaw), s = std::sin(yaw); + float rx = gx * c + gy * s; + float ry = -gx * s + gy * c; + float slopeL1 = std::abs(rx) + std::abs(ry); + totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1); + } + + return h0 + radius * totalSlope; +} + 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); diff --git a/src/common/Collision/DynamicTree.h b/src/common/Collision/DynamicTree.h index c9dd5281bb..b64577b3c2 100644 --- a/src/common/Collision/DynamicTree.h +++ b/src/common/Collision/DynamicTree.h @@ -55,6 +55,8 @@ public: float pModifyDist) const; [[nodiscard]] float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const; + [[nodiscard]] float getHeightAccurate(float x, float y, float z, float maxSearchDist, uint32 phasemask, + float radius, float yaw, float blend, float clamp, float sampleDelta) const; void insert(const GameObjectModel&); void remove(const GameObjectModel&); diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 6f6c91ce70..9c71bd1731 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1420,6 +1420,48 @@ Height.Accurate.SquareBlend = 0.2 Height.Accurate.SlopeClamp = 0.0 +# +# Gradient mode +# 0 = Plane (exact face-plane gradient; maximizes geometric accuracy) +# 1 = LS (least-squares gradient; smoother, slightly less exact) +# + +Height.Accurate.GradientMode = 0 + +# +# Normal epsilon used to detect degenerate faces (|nz| < eps) +# Recommended: 1e-6 .. 1e-5 +# + +Height.Accurate.NormalEps = 0.000001 + +# +# Accurate VMAP height (apply same footprint & slope logic on VMAP) +# 1 = enable (recommended), 0 = legacy point-ray height +# + +Height.Accurate.VMap.Enable = 1 + +# +# Lateral sample delta (meters) to estimate VMAP gradient with 4 extra rays: +# (x±delta,y) and (x,y±delta). Typical 0.15..0.35. Affects precision & cost. +# + +Height.Accurate.VMap.Delta = 0.25 + +# +# -- Accurate DYNAMIC (MMAP) planar offset (gradient sampling) -- +# 1 = enable (recommended), 0 = legacy point ray +# + +Height.Accurate.Dynamic.Enable = 1 + +# +# Gradient sampling delta for Dynamic (meters). Typical 0.15..0.35 +# + +Height.Accurate.Dynamic.Delta = 0.25 + # # DetectPosCollision # Description: Check final move position, summon position, etc for visible collision with diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 6e571885a2..9a629ee8d8 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -617,7 +617,7 @@ LiquidData const GridTerrainData::GetLiquidData(float x, float y, float z, float return liquidData; } -namespace +namespace { constexpr float INV_SQRT2 = 0.70710678118654752440f; // 1/sqrt(2) } @@ -684,21 +684,22 @@ bool GridTerrainData::SampleHeights(uint32 xInt, uint32 yInt, float& h1, float& float GridTerrainData::GetHeightAccurate(float x, float y, float radius) const { - return GetHeightAccurate(x, y, radius, GroundFootprintShape::Circle, 0.0f, 1.0f, 0.0f); + return GetHeightAccurate(x, y, radius, GroundFootprintShape::Circle, 0.0f, 1.0f, 0.0f, 0u, 1.0e-6f); } float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const { - return GetHeightAccurate(x, y, radius, shape, 0.0f, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f); + return GetHeightAccurate(x, y, radius, shape, 0.0f, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f, 0u, 1.0e-6f); } float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw) const { - return GetHeightAccurate(x, y, radius, shape, yaw, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f); + // Wrapper with default blend = 1 for circle, 0 for square. + return GetHeightAccurate(x, y, radius, shape, yaw, (shape == GroundFootprintShape::Square ? 0.0f : 1.0f), 0.0f, 0u, 1.0e-6f); } float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw, - float squareBlend, float slopeClamp) const + float squareBlend, float slopeClamp, uint32 gradientMode, float normalEps) const { if (!_loadedHeightData) return INVALID_HEIGHT; @@ -710,6 +711,7 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF int yInt = static_cast(std::floor(yf)); float fx = xf - static_cast(xInt); float fy = yf - static_cast(yInt); + if (fx < 0.0f) { fx += 1.0f; --xInt; } if (fy < 0.0f) { fy += 1.0f; --yInt; } if (fx >= 1.0f) { fx -= 1.0f; ++xInt; } @@ -767,45 +769,54 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF const G3D::Vector3 V = C - A; const G3D::Vector3 n = U.cross(V); const float nzAbs = std::abs(n.z); - if (nzAbs < eps) + if (nzAbs < std::max(eps, normalEps)) return getHeight(x, y); const float zPlane = A.z - (n.x * (P.x - A.x) + n.y * (P.y - A.y)) / n.z; const float inv2S = 1.0f / (2.0f * S); + float gx, gy; - float a = ((h2 + h4) - (h1 + h3)) * inv2S; // dz/dx - float b = ((h3 + h4) - (h1 + h2)) * inv2S; // dz/dy + if (gradientMode == 0u) + { + // Plane-exact gradient: z = (-n.x*x - n.y*y - d)/n.z => ∇z = (-n.x/n.z, -n.y/n.z) + gx = -n.x / n.z; + gy = -n.y / n.z; + } + else + { + // LS gradient (smoother) + gx = ((h2 + h4) - (h1 + h3)) * inv2S; + gy = ((h3 + h4) - (h1 + h2)) * inv2S; + } + // Optional clamp if (slopeClamp > 0.0f) { - const float g2 = a * a + b * b; + const float g2 = gx*gx + gy*gy; const float c2 = slopeClamp * slopeClamp; if (g2 > c2) { - const float scale = slopeClamp / std::sqrt(g2); - a *= scale; b *= scale; + const float s = slopeClamp / std::sqrt(g2); + gx *= s; gy *= s; } } - const float slopeL2 = std::sqrt(std::max(0.0f, a*a + b*b)); + const float slopeL2 = std::sqrt(std::max(0.0f, gx*gx + gy*gy)); + // Fast‑path radius 0 if (radius <= 0.0f) return zPlane; - float totalSlope = slopeL2; + float totalSlope = slopeL2; // por defecto: círculo (o blend==1) if (shape == GroundFootprintShape::Square && blend < 1.0f) { - float slopeL1; - if (a == 0.0f && b == 0.0f) - { - slopeL1 = 0.0f; - } - else + float slopeL1 = 0.0f; + if (!(gx == 0.0f && gy == 0.0f)) { const float c = std::cos(yaw); const float s = std::sin(yaw); - const float rx = a * c + b * s; - const float ry = -a * s + b * c; + const float rx = gx * c + gy * s; + const float ry = -gx * s + gy * c; slopeL1 = std::abs(rx) + std::abs(ry); } totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1); @@ -813,4 +824,4 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF // Final height (Minkowski): base + radius * slope return zPlane + radius * totalSlope; -} +} \ No newline at end of file diff --git a/src/server/game/Grids/GridTerrainData.h b/src/server/game/Grids/GridTerrainData.h index 14571a70ab..b5871b9db3 100644 --- a/src/server/game/Grids/GridTerrainData.h +++ b/src/server/game/Grids/GridTerrainData.h @@ -261,10 +261,12 @@ public: return "unknown"; } - float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw, float squareBlend, float slopeClamp) const; + // Accurate height with footprint & yaw. squareBlend in [0..1]. slopeClamp in [0..10] (0 disables). + float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw, + float squareBlend, float slopeClamp, uint32 gradientMode, float normalEps) const; + // Convenience wrappers (kept for compatibility). float GetHeightAccurate(float x, float y, float radius) const; float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape) const; - float GetHeightAccurate(float x, float y, float radius, GroundFootprintShape shape, float yaw /*rads*/) const; inline std::string to_string(GridTerrainData::GroundFootprintShape s) { return GridTerrainData::ToString(s); } uint16 getArea(float x, float y) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 790cefe5ac..1ad5aafcf0 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1175,6 +1175,65 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float return mapHeight; // explicitly use map data } +namespace { constexpr float INV_SQRT2 = 0.70710678118654752440f; } + +float Map::GetVMapHeightAccurate(float x, float y, float z, float radius, float yaw, + GridTerrainData::GroundFootprintShape shape, float blend, float clamp, float sampleDelta) const +{ + VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); + auto sample = [&](float sx, float sy) -> float + { + float h = vmgr->getHeight(GetId(), sx, sy, z, DEFAULT_HEIGHT_SEARCH); + return (h > INVALID_HEIGHT) ? h : std::numeric_limits::quiet_NaN(); + }; + + float h0 = sample(x, y); + if (!std::isfinite(h0)) + return VMAP_INVALID_HEIGHT_VALUE; + + if (radius <= 0.0f) + return h0; + + const float d = (sampleDelta > 0.0f) ? sampleDelta + : std::max(0.05f, std::min(0.5f, radius * 0.5f)); + + float hx1 = sample(x + d, y), hx2 = sample(x - d, y); + float hy1 = sample(x, y + d), hy2 = sample(x, y - d); + + auto diff = [&](float p, float m) -> float + { + if (std::isfinite(p) && std::isfinite(m)) return (p - m) / (2.0f * d); + if (std::isfinite(p)) return (p - h0) / d; + if (std::isfinite(m)) return (h0 - m) / d; + return 0.0f; + }; + + float gx = diff(hx1, hx2); // dz/dx + float gy = diff(hy1, hy2); // dz/dy + + if (clamp > 0.0f) + { + float g2 = gx*gx + gy*gy, c2 = clamp*clamp; + if (g2 > c2) + { + float s = clamp / std::sqrt(g2); + gx *= s; gy *= s; + } + } + + float slopeL2 = std::sqrt(std::max(0.0f, gx*gx + gy*gy)); + float totalSlope = slopeL2; + + if (shape == GridTerrainData::GroundFootprintShape::Square && blend < 1.0f) + { + float c = std::cos(yaw), s = std::sin(yaw); + float rx = gx * c + gy * s, ry = -gx * s + gy * c; + float slopeL1 = std::abs(rx) + std::abs(ry); + totalSlope = blend * slopeL2 + (1.0f - blend) * (INV_SQRT2 * slopeL1); + } + return h0 + radius * totalSlope; +} + float Map::GetHeightAccurate(float x, float y, float z, float radius, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { return GetHeightAccurate(x, y, z, radius, 0.0f, checkVMap, maxSearchDist); @@ -1195,8 +1254,20 @@ float Map::GetHeightAccurate(float x, float y, float z, float radius, float yaw, float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; if (checkVMap) { - VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); - vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); // VMAP: same criterion as GetHeight + const bool useAccurateVMap = (sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE) != 0); + if (useAccurateVMap) + { + auto shape = static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)); + float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); + float delta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA); + vmapHeight = GetVMapHeightAccurate(x, y, z, radius, yaw, shape, blend, clamp, delta); + } + else + { + VMAP::IVMapMgr* vmgr = VMAP::VMapFactory::createOrGetVMapMgr(); + vmapHeight = vmgr->getHeight(GetId(), x, y, z, maxSearchDist); + } } // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT @@ -1664,7 +1735,7 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const -{ +{ return GetHeightAccurate(phasemask, x, y, z, radius, 0.0f, vmap, maxSearchDist); } @@ -1672,7 +1743,22 @@ float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { const float hMapMix = GetHeightAccurate(x, y, z, radius, yaw, vmap, maxSearchDist); - const float hDyn = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + + float hDyn; + const bool dynAcc = (sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE) != 0); + if (dynAcc) + { + const auto shape = static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)); + const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); + const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); + const float dlt = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA); + const float effBlend = (shape == GridTerrainData::GroundFootprintShape::Square) ? blend : 1.0f; + hDyn = _dynamicTree.getHeightAccurate(x, y, z, maxSearchDist, phasemask, radius, yaw, effBlend, clamp, dlt); + } + else + { + hDyn = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); + } return std::max(hMapMix, hDyn); } diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 9222e19f52..c9f0703fc9 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -527,6 +527,9 @@ private: bool EnsureGridLoaded(Cell const& cell); MapGridType* GetMapGrid(uint16 const x, uint16 const y); + [[nodiscard]] float GetVMapHeightAccurate(float x, float y, float z, float radius, float yaw, + GridTerrainData::GroundFootprintShape shape, float blend, float clamp, float sampleDelta) const; + void ScriptsProcess(); void SendObjectUpdates(); diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 5a49dc091c..ce7e89f2a7 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -286,6 +286,12 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE, "Height.Accurate.RadiusScale", 1.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.1f && value <= 4.0f; }, ">= 0.1 and <= 4.0"); SetConfigValue(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, "Height.Accurate.SquareBlend", 0.20f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 1.0f; }, ">= 0.0 and <= 1.0"); SetConfigValue(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP, "Height.Accurate.SlopeClamp", 0.0f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.0f && value <= 10.0f; }, ">= 0.0 and <= 10.0"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE, "Height.Accurate.GradientMode", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_NORMAL_EPS, "Height.Accurate.NormalEps", 1.0e-6f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 1e-8f && value <= 1e-3f; }, ">= 1e-8f and <= 1e-3f"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE, "Height.Accurate.VMap.Enable", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA, "Height.Accurate.VMap.Delta", 0.25f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.01f && value <= 1.0f; }, ">= 0.01f and <= 1.0f"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE, "Height.Accurate.Dynamic.Enable", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 1; }, "<= 1"); + SetConfigValue(CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA, "Height.Accurate.Dynamic.Delta", 0.25f, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0.01f && value <= 1.0f; }, ">= 0.01f and <= 1.0f"); SetConfigValue(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 563b724294..e8318d6c3b 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -485,6 +485,12 @@ enum ServerConfigs CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND, CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP, CONFIG_HEIGHT_ACCURATE_SHAPE, + CONFIG_HEIGHT_ACCURATE_NORMAL_EPS, + CONFIG_HEIGHT_ACCURATE_VMAP_DELTA, + CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA, + CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE, + CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE, + CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE, MAX_NUM_SERVER_CONFIGS }; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 24ae53a8ac..f0deac86cc 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -633,6 +633,10 @@ public: const float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); + const uint32 vmapAcc = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE); + const float vdelta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA); + const uint32 dynAcc = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE); + const float dyndelta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_DELTA); const float yaw = object->GetOrientation(); const float probeRScaled = probeR * rScale; @@ -669,7 +673,8 @@ public: cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), object->GetInstanceId(), zoneX, zoneY, groundZ, floorZ, haveMap, haveVMap, haveMMAP); - handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f}, SlopeClamp: {:0.2f})", shapeStr, blend, rScale, clamp); + handler->PSendSysMessage("Accurate Height Shape: {} (SquareBlend: {:0.2f}, RadiusScale: {:0.2f}, SlopeClamp: {:0.2f}, GradMode: {}, NormalEps: {:0.6f}, VMapAccurate: {}, VMapDelta: {:0.2f}, DynAccurate: {}, DynDelta: {:0.2f})", + shapeStr, blend, rScale, clamp, gmode, neps, vmapAcc, vdelta, dynAcc, dyndelta); handler->PSendSysMessage("Accurate Height Grid: {}", GridZAccurate); handler->PSendSysMessage("Accurate Height Map: {}", MapZAccurate); handler->PSendSysMessage("Probe radius (pre-scale): {:0.3f}", probeR); From 19f31152f3c76f65ccabc23ee79b4643fdc1cb73 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Thu, 25 Sep 2025 17:29:27 +0200 Subject: [PATCH 26/27] . --- src/server/game/Grids/GridTerrainData.cpp | 4 ++-- src/server/game/Maps/Map.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/game/Grids/GridTerrainData.cpp b/src/server/game/Grids/GridTerrainData.cpp index 9a629ee8d8..943fe0e286 100644 --- a/src/server/game/Grids/GridTerrainData.cpp +++ b/src/server/game/Grids/GridTerrainData.cpp @@ -617,7 +617,7 @@ LiquidData const GridTerrainData::GetLiquidData(float x, float y, float z, float return liquidData; } -namespace +namespace { constexpr float INV_SQRT2 = 0.70710678118654752440f; // 1/sqrt(2) } @@ -824,4 +824,4 @@ float GridTerrainData::GetHeightAccurate(float x, float y, float radius, GroundF // Final height (Minkowski): base + radius * slope return zPlane + radius * totalSlope; -} \ No newline at end of file +} diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 1ad5aafcf0..907a5969ba 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1735,7 +1735,7 @@ float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=tr float Map::GetHeightAccurate(uint32 phasemask, float x, float y, float z, float radius, bool vmap/*=true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const -{ +{ return GetHeightAccurate(phasemask, x, y, z, radius, 0.0f, vmap, maxSearchDist); } From 49ebc55bdaadda2d82cfe04d760771edf9add977 Mon Sep 17 00:00:00 2001 From: VG-prog Date: Thu, 25 Sep 2025 18:23:04 +0200 Subject: [PATCH 27/27] . --- src/server/game/Maps/Map.cpp | 4 +++- src/server/scripts/Commands/cs_misc.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 907a5969ba..d101c2a7d2 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1307,7 +1307,9 @@ float Map::GetGridHeightAccurate(float x, float y, float radius, float yaw) cons const auto shape = static_cast(sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_SHAPE)); const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); - return gmap->GetHeightAccurate(x, y, radius, shape, yaw, blend, clamp); + const uint32 mode = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE); + const float eps = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_NORMAL_EPS); + return gmap->GetHeightAccurate(x, y, radius, shape, yaw, blend, clamp, mode, eps); } return INVALID_HEIGHT; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index f0deac86cc..fbd92f15e8 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -633,6 +633,8 @@ public: const float rScale = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_RADIUS_SCALE); const float blend = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SQUARE_BLEND); const float clamp = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_SLOPE_CLAMP); + const uint32 gmode = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_GRADIENT_MODE); + const float neps = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_NORMAL_EPS); const uint32 vmapAcc = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_VMAP_ENABLE); const float vdelta = sWorld->getFloatConfig(CONFIG_HEIGHT_ACCURATE_VMAP_DELTA); const uint32 dynAcc = sWorld->getIntConfig(CONFIG_HEIGHT_ACCURATE_DYNAMIC_ENABLE);