Files
TrinityCore/src/server/scripts/Commands/cs_mmaps.cpp
T
Giacomo Pozzoni c0b75bf40d Core/MMAPs: Adjust walkable climb and fix a lot of mmap raycast issues (#24539)
* Core/MMAPs: Adjust walkable climb

Adjust walkable climb as recast using walkableClimb also to find the poly from a position, giving priority to polys that are below the position but closer than walkableClimb.

* Temporarily disable static collision (the whole check should be removed)

* Core/Spells: removed deprecated mmap path check for TARGET_DEST_CASTER_FRONT_LEAP

* Core/Objects: allow flying units to use the helper as well (flying units casting radius based spells)

* Code refactor

* Handle raycasts that end in a point with no height in the mmap mesh as PATHFIND_NOPATH

* Walk back a bit from raycast hitpoints as sometime the 2D result point is outside of the polygons due to floating point errors.

* Remove whitespace

* Revert 4a197ba22a as a raycast point path should have the Z retrieved with getPolyHeight(). Raycast will only return a 2-point path with Start and Hitpoint/End

* Cleanup PathGenerator raycast case

* Fix PathGenerator raycast broken if start and end are on same poly.
Fix PathGenerator raycast broken if no wall is hit.
Remove unused case of using raycast with an existing previous path (can be added back properly if needed).
Remove forcing poly length to 2 when we actually have already the right number.

* Use closestPointOnPolyBoundary on the second try of finding a point on poly for raycast.
Note that in this case the mesh height is not used which might cause issues. The poly boundary height will be used instead.

* Handle cases where getPolyHeight() fails because the point is on polygon border (and caused by floating point imprecision)

* Add far from poly flags

* Set PATHFIND_INCOMPLETE in raycast case if startFarFromPoly or endFarFromPoly

* Fix blink close to walls with no valid polygon behind the wall

* Require to re-extract mmaps

Co-authored-by: Ovah <dreadkiller@gmx.de>
2020-05-15 20:43:12 +02:00

299 lines
11 KiB
C++

/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file cs_mmaps.cpp
* @brief .mmap related commands
*
* This file contains the CommandScripts for all
* mmap sub-commands
*/
#include "ScriptMgr.h"
#include "CellImpl.h"
#include "Chat.h"
#include "DisableMgr.h"
#include "GridNotifiersImpl.h"
#include "Map.h"
#include "MMapFactory.h"
#include "PathGenerator.h"
#include "Player.h"
#include "PointMovementGenerator.h"
#include "RBAC.h"
class mmaps_commandscript : public CommandScript
{
public:
mmaps_commandscript() : CommandScript("mmaps_commandscript") { }
std::vector<ChatCommand> GetCommands() const override
{
static std::vector<ChatCommand> mmapCommandTable =
{
{ "loadedtiles", rbac::RBAC_PERM_COMMAND_MMAP_LOADEDTILES, false, &HandleMmapLoadedTilesCommand, "" },
{ "loc", rbac::RBAC_PERM_COMMAND_MMAP_LOC, false, &HandleMmapLocCommand, "" },
{ "path", rbac::RBAC_PERM_COMMAND_MMAP_PATH, false, &HandleMmapPathCommand, "" },
{ "stats", rbac::RBAC_PERM_COMMAND_MMAP_STATS, false, &HandleMmapStatsCommand, "" },
{ "testarea", rbac::RBAC_PERM_COMMAND_MMAP_TESTAREA, false, &HandleMmapTestArea, "" },
};
static std::vector<ChatCommand> commandTable =
{
{ "mmap", rbac::RBAC_PERM_COMMAND_MMAP, true, nullptr, "", mmapCommandTable },
};
return commandTable;
}
static bool HandleMmapPathCommand(ChatHandler* handler, char const* args)
{
if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()))
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
return true;
}
handler->PSendSysMessage("mmap path:");
// units
Player* player = handler->GetSession()->GetPlayer();
Unit* target = handler->getSelectedUnit();
if (!player || !target)
{
handler->PSendSysMessage("Invalid target/source selection.");
return true;
}
char* para = strtok((char*)args, " ");
bool useStraightPath = false;
if (para && strcmp(para, "true") == 0)
useStraightPath = true;
bool useRaycast = false;
if (para && (strcmp(para, "line") == 0 || strcmp(para, "ray") == 0 || strcmp(para, "raycast") == 0))
useRaycast = true;
// unit locations
float x, y, z;
player->GetPosition(x, y, z);
// path
PathGenerator path(target);
path.SetUseStraightPath(useStraightPath);
path.SetUseRaycast(useRaycast);
bool result = path.CalculatePath(x, y, z, false);
Movement::PointsArray const& pointPath = path.GetPath();
handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str());
handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : useRaycast ? "Raycast" : "SmoothPath");
handler->PSendSysMessage("Result: %s - Length: %zu - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType());
G3D::Vector3 const& start = path.GetStartPosition();
G3D::Vector3 const& end = path.GetEndPosition();
G3D::Vector3 const& actualEnd = path.GetActualEndPosition();
handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z);
handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z);
handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z);
if (!player->IsGameMaster())
handler->PSendSysMessage("Enable GM mode to see the path points.");
for (uint32 i = 0; i < pointPath.size(); ++i)
player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000);
return true;
}
static bool HandleMmapLocCommand(ChatHandler* handler, char const* /*args*/)
{
handler->PSendSysMessage("mmap tileloc:");
// grid tile location
Player* player = handler->GetSession()->GetPlayer();
int32 gx = 32 - player->GetPositionX() / SIZE_OF_GRIDS;
int32 gy = 32 - player->GetPositionY() / SIZE_OF_GRIDS;
handler->PSendSysMessage("%03u%02i%02i.mmtile", player->GetMapId(), gx, gy);
handler->PSendSysMessage("gridloc [%i, %i]", gy, gx);
// calculate navmesh tile location
dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId());
if (!navmesh || !navmeshquery)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
return true;
}
float const* min = navmesh->getParams()->orig;
float x, y, z;
player->GetPosition(x, y, z);
float location[VERTEX_SIZE] = {y, z, x};
float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f};
int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS);
int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS);
handler->PSendSysMessage("Calc [%02i, %02i]", tilex, tiley);
// navmesh poly -> navmesh tile location
dtQueryFilter filter = dtQueryFilter();
dtPolyRef polyRef = INVALID_POLYREF;
if (dtStatusFailed(navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, nullptr)))
{
handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)");
return true;
}
if (polyRef == INVALID_POLYREF)
handler->PSendSysMessage("Dt [??, ??] (invalid poly, probably no tile loaded)");
else
{
dtMeshTile const* tile;
dtPoly const* poly;
if (dtStatusSucceed(navmesh->getTileAndPolyByRef(polyRef, &tile, &poly)))
{
if (tile)
{
handler->PSendSysMessage("Dt [%02i,%02i]", tile->header->x, tile->header->y);
return false;
}
}
handler->PSendSysMessage("Dt [??,??] (no tile loaded)");
}
return true;
}
static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/)
{
uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId();
dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid);
dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId());
if (!navmesh || !navmeshquery)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
return true;
}
handler->PSendSysMessage("mmap loadedtiles:");
for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
{
dtMeshTile const* tile = navmesh->getTile(i);
if (!tile || !tile->header)
continue;
handler->PSendSysMessage("[%02i, %02i]", tile->header->x, tile->header->y);
}
return true;
}
static bool HandleMmapStatsCommand(ChatHandler* handler, char const* /*args*/)
{
uint32 mapId = handler->GetSession()->GetPlayer()->GetMapId();
handler->PSendSysMessage("mmap stats:");
handler->PSendSysMessage(" global mmap pathfinding is %sabled", DisableMgr::IsPathfindingEnabled(mapId) ? "en" : "dis");
MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager();
handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount());
dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId());
if (!navmesh)
{
handler->PSendSysMessage("NavMesh not loaded for current map.");
return true;
}
uint32 tileCount = 0;
uint32 nodeCount = 0;
uint32 polyCount = 0;
uint32 vertCount = 0;
uint32 triCount = 0;
uint32 triVertCount = 0;
uint32 dataSize = 0;
for (int32 i = 0; i < navmesh->getMaxTiles(); ++i)
{
dtMeshTile const* tile = navmesh->getTile(i);
if (!tile || !tile->header)
continue;
tileCount++;
nodeCount += tile->header->bvNodeCount;
polyCount += tile->header->polyCount;
vertCount += tile->header->vertCount;
triCount += tile->header->detailTriCount;
triVertCount += tile->header->detailVertCount;
dataSize += tile->dataSize;
}
handler->PSendSysMessage("Navmesh stats:");
handler->PSendSysMessage(" %u tiles loaded", tileCount);
handler->PSendSysMessage(" %u BVTree nodes", nodeCount);
handler->PSendSysMessage(" %u polygons (%u vertices)", polyCount, vertCount);
handler->PSendSysMessage(" %u triangles (%u vertices)", triCount, triVertCount);
handler->PSendSysMessage(" %.2f MB of data (not including pointers)", ((float)dataSize / sizeof(unsigned char)) / 1048576);
return true;
}
static bool HandleMmapTestArea(ChatHandler* handler, char const* /*args*/)
{
float radius = 40.0f;
WorldObject* object = handler->GetSession()->GetPlayer();
// Get Creatures
std::list<Creature*> creatureList;
Trinity::AnyUnitInObjectRangeCheck go_check(object, radius);
Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck> go_search(object, creatureList, go_check);
Cell::VisitGridObjects(object, go_search, radius);
if (!creatureList.empty())
{
handler->PSendSysMessage("Found %zu Creatures.", creatureList.size());
uint32 paths = 0;
uint32 uStartTime = getMSTime();
float gx, gy, gz;
object->GetPosition(gx, gy, gz);
for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr)
{
PathGenerator path(*itr);
path.CalculatePath(gx, gy, gz);
++paths;
}
uint32 uPathLoadTime = getMSTimeDiff(uStartTime, getMSTime());
handler->PSendSysMessage("Generated %i paths in %i ms", paths, uPathLoadTime);
}
else
handler->PSendSysMessage("No creatures in %f yard range.", radius);
return true;
}
};
void AddSC_mmaps_commandscript()
{
new mmaps_commandscript();
}