aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/collision/CMakeLists.txt2
-rw-r--r--src/server/collision/Management/MMapFactory.cpp80
-rw-r--r--src/server/collision/Management/MMapFactory.h51
-rw-r--r--src/server/collision/Management/MMapManager.cpp298
-rw-r--r--src/server/collision/Management/MMapManager.h84
-rwxr-xr-xsrc/server/collision/Management/VMapManager2.h2
-rwxr-xr-xsrc/server/collision/Maps/MapTree.h1
-rwxr-xr-xsrc/server/collision/Models/ModelInstance.h2
-rwxr-xr-xsrc/server/collision/Models/WorldModel.h6
-rw-r--r--src/server/game/CMakeLists.txt2
-rwxr-xr-xsrc/server/game/Entities/Unit/Unit.cpp12
-rwxr-xr-xsrc/server/game/Entities/Unit/Unit.h3
-rwxr-xr-xsrc/server/game/Entities/Vehicle/Vehicle.cpp2
-rwxr-xr-xsrc/server/game/Handlers/MovementHandler.cpp2
-rwxr-xr-xsrc/server/game/Maps/Map.cpp36
-rwxr-xr-xsrc/server/game/Maps/Map.h3
-rwxr-xr-xsrc/server/game/Maps/MapInstanced.cpp2
-rwxr-xr-xsrc/server/game/Miscellaneous/SharedDefines.h30
-rw-r--r--[-rwxr-xr-x]src/server/game/Movement/MotionMaster.cpp54
-rwxr-xr-xsrc/server/game/Movement/MotionMaster.h4
-rwxr-xr-xsrc/server/game/Movement/MovementGenerator.h38
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp143
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h14
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp376
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h28
-rw-r--r--[-rwxr-xr-x]src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp48
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/HomeMovementGenerator.h10
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp54
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/IdleMovementGenerator.h26
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp99
-rw-r--r--[-rwxr-xr-x]src/server/game/Movement/MovementGenerators/PointMovementGenerator.h29
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp52
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/RandomMovementGenerator.h12
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp184
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h60
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp424
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h32
-rw-r--r--src/server/game/Movement/PathGenerator.cpp808
-rw-r--r--src/server/game/Movement/PathGenerator.h137
-rw-r--r--src/server/game/Movement/Spline/MoveSpline.h3
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.cpp75
-rw-r--r--src/server/game/Movement/Spline/MoveSplineInit.h23
-rwxr-xr-xsrc/server/game/Movement/Waypoints/Path.h24
-rwxr-xr-xsrc/server/game/World/World.cpp10
-rw-r--r--[-rwxr-xr-x]src/server/game/World/World.h1
-rw-r--r--src/server/scripts/CMakeLists.txt2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp2
-rw-r--r--src/server/shared/CMakeLists.txt1
-rw-r--r--src/server/shared/Memory.h17
-rw-r--r--src/server/worldserver/CMakeLists.txt2
-rw-r--r--src/server/worldserver/worldserver.conf.dist15
-rw-r--r--src/tools/CMakeLists.txt1
-rw-r--r--src/tools/map_extractor/System.cpp42
-rw-r--r--src/tools/mmaps_generator/CMakeLists.txt68
-rw-r--r--src/tools/mmaps_generator/Info/readme.txt66
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.cpp277
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.h53
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp970
-rw-r--r--src/tools/mmaps_generator/MapBuilder.h154
-rw-r--r--src/tools/mmaps_generator/PathCommon.h128
-rw-r--r--src/tools/mmaps_generator/PathGenerator.cpp297
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.cpp856
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.h138
-rw-r--r--src/tools/mmaps_generator/VMapExtensions.cpp72
64 files changed, 5338 insertions, 1209 deletions
diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt
index 9fc696ab19a..d54d5f91046 100644
--- a/src/server/collision/CMakeLists.txt
+++ b/src/server/collision/CMakeLists.txt
@@ -34,7 +34,9 @@ set(collision_STAT_SRCS
include_directories(
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
diff --git a/src/server/collision/Management/MMapFactory.cpp b/src/server/collision/Management/MMapFactory.cpp
new file mode 100644
index 00000000000..f4b2f3d47e4
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#include "MMapFactory.h"
+#include "World.h"
+#include "Config.h"
+#include <set>
+
+namespace MMAP
+{
+ // ######################## MMapFactory ########################
+ // our global singleton copy
+ MMapManager *g_MMapManager = NULL;
+
+ // stores list of mapids which do not use pathfinding
+ std::set<uint32>* g_mmapDisabledIds = NULL;
+
+ MMapManager* MMapFactory::createOrGetMMapManager()
+ {
+ if (g_MMapManager == NULL)
+ g_MMapManager = new MMapManager();
+
+ return g_MMapManager;
+ }
+
+ void MMapFactory::preventPathfindingOnMaps(const char* ignoreMapIds)
+ {
+ if (!g_mmapDisabledIds)
+ g_mmapDisabledIds = new std::set<uint32>();
+
+ uint32 strLenght = strlen(ignoreMapIds)+1;
+ char* mapList = new char[strLenght];
+ memcpy(mapList, ignoreMapIds, sizeof(char)*strLenght);
+
+ char* idstr = strtok(mapList, ",");
+ while (idstr)
+ {
+ g_mmapDisabledIds->insert(uint32(atoi(idstr)));
+ idstr = strtok(NULL, ",");
+ }
+
+ delete[] mapList;
+ }
+
+ bool MMapFactory::IsPathfindingEnabled(uint32 mapId)
+ {
+ return sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS)
+ && g_mmapDisabledIds->find(mapId) == g_mmapDisabledIds->end();
+ }
+
+ void MMapFactory::clear()
+ {
+ if (g_mmapDisabledIds)
+ {
+ delete g_mmapDisabledIds;
+ g_mmapDisabledIds = NULL;
+ }
+
+ if (g_MMapManager)
+ {
+ delete g_MMapManager;
+ g_MMapManager = NULL;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/server/collision/Management/MMapFactory.h b/src/server/collision/Management/MMapFactory.h
new file mode 100644
index 00000000000..ab047333a19
--- /dev/null
+++ b/src/server/collision/Management/MMapFactory.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#ifndef _MMAP_FACTORY_H
+#define _MMAP_FACTORY_H
+
+#include "MMapManager.h"
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+namespace MMAP
+{
+ enum MMAP_LOAD_RESULT
+ {
+ MMAP_LOAD_RESULT_ERROR,
+ MMAP_LOAD_RESULT_OK,
+ MMAP_LOAD_RESULT_IGNORED,
+ };
+
+ // static class
+ // holds all mmap global data
+ // access point to MMapManager singleton
+ class MMapFactory
+ {
+ public:
+ static MMapManager* createOrGetMMapManager();
+ static void clear();
+ static void preventPathfindingOnMaps(const char* ignoreMapIds);
+ static bool IsPathfindingEnabled(uint32 mapId);
+ };
+}
+
+#endif
+
diff --git a/src/server/collision/Management/MMapManager.cpp b/src/server/collision/Management/MMapManager.cpp
new file mode 100644
index 00000000000..0222f1a5995
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#include "MMapManager.h"
+#include "Log.h"
+#include "World.h"
+
+namespace MMAP
+{
+ // ######################## MMapManager ########################
+ MMapManager::~MMapManager()
+ {
+ for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
+ delete i->second;
+
+ // by now we should not have maps loaded
+ // if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
+ }
+
+ bool MMapManager::loadMapData(uint32 mapId)
+ {
+ // we already have this map loaded?
+ if (loadedMMaps.find(mapId) != loadedMMaps.end())
+ return true;
+
+ // load and init dtNavMesh - read parameters from file
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1;
+ char *fileName = new char[pathLen];
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId);
+
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ dtNavMeshParams params;
+ fread(&params, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+
+ dtNavMesh* mesh = dtAllocNavMesh();
+ ASSERT(mesh);
+ if (DT_SUCCESS != mesh->init(&params))
+ {
+ dtFreeNavMesh(mesh);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
+ delete [] fileName;
+ return false;
+ }
+
+ delete [] fileName;
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMapData: Loaded %03i.mmap", mapId);
+
+ // store inside our map list
+ MMapData* mmap_data = new MMapData(mesh);
+ mmap_data->mmapLoadedTiles.clear();
+
+ loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data));
+ return true;
+ }
+
+ uint32 MMapManager::packTileID(int32 x, int32 y)
+ {
+ return uint32(x << 16 | y);
+ }
+
+ bool MMapManager::loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y)
+ {
+ // make sure the mmap is loaded and ready to load tiles
+ if (!loadMapData(mapId))
+ return false;
+
+ // get this mmap data
+ MMapData* mmap = loadedMMaps[mapId];
+ ASSERT(mmap->navMesh);
+
+ // check if we already have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
+ return false;
+
+ // load this tile :: mmaps/MMMXXYY.mmtile
+ uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1;
+ char *fileName = new char[pathLen];
+ // this change of y and x is needed because of mapbuilder.cpp (x and y are swapped there) so change it here so we dont need to re-extract. All swappes are done in other files. DONT CHANGE THIS!
+ snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, y, x);
+
+ FILE *file = fopen(fileName, "rb");
+ if (!file)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
+ delete [] fileName;
+ return false;
+ }
+ delete [] fileName;
+
+ // read header
+ MmapTileHeader fileHeader;
+ fread(&fileHeader, sizeof(MmapTileHeader), 1, file);
+
+ if (fileHeader.mmapMagic != MMAP_MAGIC)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, y, x);
+ return false;
+ }
+
+ if (fileHeader.mmapVersion != MMAP_VERSION)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
+ mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
+ return false;
+ }
+
+ unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
+ ASSERT(data);
+
+ size_t result = fread(data, fileHeader.size, 1, file);
+ if (!result)
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, y, x);
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ dtMeshHeader* header = (dtMeshHeader*)data;
+ dtTileRef tileRef = 0;
+
+ // memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
+ if (DT_SUCCESS == mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef))
+ {
+ mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
+ ++loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:loadMap: Loaded mmtile %03i[%02i,%02i] into %03i[%02i,%02i]", mapId, y, x, mapId, header->x, header->y);
+ return true;
+ }
+ else
+ {
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, y, x);
+ dtFree(data);
+ return false;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, y, x);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+
+ // check if we have this tile loaded
+ uint32 packedGridPos = packTileID(x, y);
+ if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, y, x);
+ return false;
+ }
+
+ dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
+
+ // unload, and mark as non loaded
+ if (DT_SUCCESS != mmap->navMesh->removeTile(tileRef, NULL, NULL))
+ {
+ // this is technically a memory leak
+ // if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
+ // we cannot recover from this error - assert out
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, y, x);
+ ASSERT(false);
+ }
+ else
+ {
+ mmap->mmapLoadedTiles.erase(packedGridPos);
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, y, x, mapId);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool MMapManager::unloadMap(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ // unload all tiles from given map
+ MMapData* mmap = loadedMMaps[mapId];
+ for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
+ {
+ uint32 x = (i->first >> 16);
+ uint32 y = (i->first & 0x0000FFFF);
+ if (DT_SUCCESS != mmap->navMesh->removeTile(i->second, NULL, NULL))
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, y, x);
+ else
+ {
+ --loadedTiles;
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, y, x, mapId);
+ }
+ }
+
+ delete mmap;
+ loadedMMaps.erase(mapId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded %03i.mmap", mapId);
+
+ return true;
+ }
+
+ bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
+ {
+ // check if we have this map loaded
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ {
+ // file may not exist, therefore not loaded
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
+ return false;
+ }
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
+ return false;
+ }
+
+ dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
+
+ dtFreeNavMeshQuery(query);
+ mmap->navMeshQueries.erase(instanceId);
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
+
+ return true;
+ }
+
+ dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ return loadedMMaps[mapId]->navMesh;
+ }
+
+ dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
+ {
+ if (loadedMMaps.find(mapId) == loadedMMaps.end())
+ return NULL;
+
+ MMapData* mmap = loadedMMaps[mapId];
+ if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
+ {
+ // allocate mesh query
+ dtNavMeshQuery* query = dtAllocNavMeshQuery();
+ ASSERT(query);
+ if (DT_SUCCESS != query->init(mmap->navMesh, 1024))
+ {
+ dtFreeNavMeshQuery(query);
+ sLog->outError(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ return NULL;
+ }
+
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
+ mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
+ }
+
+ return mmap->navMeshQueries[instanceId];
+ }
+} \ No newline at end of file
diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h
new file mode 100644
index 00000000000..23b7d5fc6f8
--- /dev/null
+++ b/src/server/collision/Management/MMapManager.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#ifndef _MMAP_MANAGER_H
+#define _MMAP_MANAGER_H
+
+#include "UnorderedMap.h"
+#include "DetourAlloc.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+
+// move map related classes
+namespace MMAP
+{
+ typedef UNORDERED_MAP<uint32, dtTileRef> MMapTileSet;
+ typedef UNORDERED_MAP<uint32, dtNavMeshQuery*> NavMeshQuerySet;
+
+ // dummy struct to hold map's mmap data
+ struct MMapData
+ {
+ MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
+ ~MMapData()
+ {
+ for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
+ dtFreeNavMeshQuery(i->second);
+
+ if (navMesh)
+ dtFreeNavMesh(navMesh);
+ }
+
+ dtNavMesh* navMesh;
+
+ // we have to use single dtNavMeshQuery for every instance, since those are not thread safe
+ NavMeshQuerySet navMeshQueries; // instanceId to query
+ MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
+ };
+
+
+ typedef UNORDERED_MAP<uint32, MMapData*> MMapDataSet;
+
+ // singleton class
+ // holds all all access to mmap loading unloading and meshes
+ class MMapManager
+ {
+ public:
+ MMapManager() : loadedTiles(0) {}
+ ~MMapManager();
+
+ bool loadMap(const std::string& basePath, uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId, int32 x, int32 y);
+ bool unloadMap(uint32 mapId);
+ bool unloadMapInstance(uint32 mapId, uint32 instanceId);
+
+ // the returned [dtNavMeshQuery const*] is NOT threadsafe
+ dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
+ dtNavMesh const* GetNavMesh(uint32 mapId);
+
+ uint32 getLoadedTilesCount() const { return loadedTiles; }
+ uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
+ private:
+ bool loadMapData(uint32 mapId);
+ uint32 packTileID(int32 x, int32 y);
+
+ MMapDataSet loadedMMaps;
+ uint32 loadedTiles;
+ };
+}
+
+#endif \ No newline at end of file
diff --git a/src/server/collision/Management/VMapManager2.h b/src/server/collision/Management/VMapManager2.h
index 1fba108388a..c87c42fd832 100755
--- a/src/server/collision/Management/VMapManager2.h
+++ b/src/server/collision/Management/VMapManager2.h
@@ -112,6 +112,8 @@ namespace VMAP
return getMapFileName(mapId);
}
virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y);
+ public:
+ void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
};
}
diff --git a/src/server/collision/Maps/MapTree.h b/src/server/collision/Maps/MapTree.h
index 1732209c6bc..f0df713bf57 100755
--- a/src/server/collision/Maps/MapTree.h
+++ b/src/server/collision/Maps/MapTree.h
@@ -80,6 +80,7 @@ namespace VMAP
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
+ void getModelInstances(ModelInstance* &models, uint32 &count);
};
struct AreaInfo
diff --git a/src/server/collision/Models/ModelInstance.h b/src/server/collision/Models/ModelInstance.h
index 2745628ac7e..2abd83ba377 100755
--- a/src/server/collision/Models/ModelInstance.h
+++ b/src/server/collision/Models/ModelInstance.h
@@ -74,6 +74,8 @@ namespace VMAP
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel* iModel;
+ public:
+ WorldModel* const getWorldModel();
};
} // namespace VMAP
diff --git a/src/server/collision/Models/WorldModel.h b/src/server/collision/Models/WorldModel.h
index ebf828e4935..98be3494927 100755
--- a/src/server/collision/Models/WorldModel.h
+++ b/src/server/collision/Models/WorldModel.h
@@ -66,6 +66,8 @@ namespace VMAP
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
+ public:
+ void getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const;
};
/*! holding additional info for WMO group files */
@@ -98,6 +100,8 @@ namespace VMAP
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid* iLiquid;
+ public:
+ void getMeshData(std::vector<Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
@@ -117,6 +121,8 @@ namespace VMAP
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
+ public:
+ void getGroupModels(std::vector<GroupModel> &groupModels);
};
} // namespace VMAP
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index 68ae2d8ea38..0f8bec7f0e5 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -108,6 +108,8 @@ set(game_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index c3fafa873bc..d9798571de5 100755
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -370,10 +370,10 @@ bool Unit::haveOffhandWeapon() const
return m_canDualWield;
}
-void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed)
+void Unit::MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath, bool forceDestination)
{
- Movement::MoveSplineInit init(*this);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(this);
+ init.MoveTo(x, y, z, generatePath, forceDestination);
init.SetVelocity(speed);
init.Launch();
}
@@ -14751,7 +14751,7 @@ void Unit::StopMoving()
if (!IsInWorld())
return;
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(GetOrientation());
init.Launch();
@@ -17153,7 +17153,7 @@ void Unit::_ExitVehicle(Position const* exitPosition)
SendMessageToSet(&data, false);
}
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ());
init.SetFacing(GetOrientation());
init.SetTransportExit();
@@ -17563,7 +17563,7 @@ void Unit::SetInFront(Unit const* target)
void Unit::SetFacingTo(float ori)
{
- Movement::MoveSplineInit init(*this);
+ Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset());
init.SetFacing(ori);
init.Launch();
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 2ec3ffec8ef..5c29537db40 100755
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -498,6 +498,7 @@ enum UnitState
UNIT_STATE_FLEEING_MOVE = 0x02000000,
UNIT_STATE_CHASE_MOVE = 0x04000000,
UNIT_STATE_FOLLOW_MOVE = 0x08000000,
+ UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_UNATTACKABLE = (UNIT_STATE_IN_FLIGHT | UNIT_STATE_ONVEHICLE),
// for real move using movegen check and stop (except unstoppable flight)
UNIT_STATE_MOVING = UNIT_STATE_ROAMING_MOVE | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE | UNIT_STATE_CHASE_MOVE | UNIT_STATE_FOLLOW_MOVE ,
@@ -1603,7 +1604,7 @@ class Unit : public WorldObject
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject* obj, float speedZ);
- void MonsterMoveWithSpeed(float x, float y, float z, float speed);
+ void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false);
//void SetFacing(float ori, WorldObject* obj = NULL);
//void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL);
void SendMovementFlagUpdate();
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index 710821f2f24..61e3fd877b4 100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -365,7 +365,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId)
unit->SendClearTarget(); // SMSG_BREAK_TARGET
unit->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures)
// also adds MOVEMENTFLAG_ROOT
- Movement::MoveSplineInit init(*unit);
+ Movement::MoveSplineInit init(unit);
init.DisableTransportPathTransformations();
init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ);
init.SetFacing(0.0f);
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 27b1f2eca3f..ca80921a7b2 100755
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -125,7 +125,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
{
// short preparations to continue flight
FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top());
- flight->Initialize(*GetPlayer());
+ flight->Initialize(GetPlayer());
return;
}
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index c06c7414e6e..684977f9563 100755
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -20,6 +20,7 @@
#include "GridStates.h"
#include "ScriptMgr.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "MapInstanced.h"
#include "CellImpl.h"
#include "GridNotifiers.h"
@@ -41,7 +42,7 @@ union u_map_magic
};
u_map_magic MapMagic = { {'M','A','P','S'} };
-u_map_magic MapVersionMagic = { {'v','1','.','2'} };
+u_map_magic MapVersionMagic = { {'v','1','.','3'} };
u_map_magic MapAreaMagic = { {'A','R','E','A'} };
u_map_magic MapHeightMagic = { {'M','H','G','T'} };
u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
@@ -69,6 +70,9 @@ Map::~Map()
if (!m_scriptSchedule.empty())
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId());
}
bool Map::ExistMap(uint32 mapid, int gx, int gy)
@@ -117,6 +121,17 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy)
return true;
}
+void Map::LoadMMap(int gx, int gy)
+{
+ // DONT CHANGE "gy" and "gx" - Its necessary !
+ bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gy, gx);
+
+ if (mmapLoadResult)
+ sLog->outInfo(LOG_FILTER_MAPS, "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+ else
+ sLog->outInfo(LOG_FILTER_MAPS, "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
+}
+
void Map::LoadVMap(int gx, int gy)
{
// x and y are swapped !!
@@ -165,18 +180,16 @@ void Map::LoadMap(int gx, int gy, bool reload)
}
// map file name
- char *tmp=NULL;
- int len = sWorld->GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
+ char* tmp = NULL;
+ int len = sWorld->GetDataPath().length() + strlen("maps/%03u%02u%02u.map") + 1;
tmp = new char[len];
- snprintf(tmp, len, (char *)(sWorld->GetDataPath()+"maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
+ snprintf(tmp, len, (char *)(sWorld->GetDataPath() + "maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy);
sLog->outInfo(LOG_FILTER_MAPS, "Loading map %s", tmp);
// loading data
GridMaps[gx][gy] = new GridMap();
if (!GridMaps[gx][gy]->loadData(tmp))
- {
sLog->outError(LOG_FILTER_MAPS, "Error loading map file: \n %s\n", tmp);
- }
- delete [] tmp;
+ delete[] tmp;
sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy);
}
@@ -184,8 +197,12 @@ void Map::LoadMap(int gx, int gy, bool reload)
void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
+ // Only load the data for the base map
if (i_InstanceId == 0)
- LoadVMap(gx, gy); // Only load the data for the base map
+ {
+ LoadVMap(gx, gy);
+ LoadMMap(gx, gy);
+ }
}
void Map::InitStateMachine()
@@ -996,6 +1013,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll)
}
// x and y are swapped
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy);
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy);
}
else
((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy));
@@ -1067,7 +1085,7 @@ GridMap::~GridMap()
unloadData();
}
-bool GridMap::loadData(char *filename)
+bool GridMap::loadData(char* filename)
{
// Unload old data if exist
unloadData();
diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h
index 0743c4e545f..126d0855352 100755
--- a/src/server/game/Maps/Map.h
+++ b/src/server/game/Maps/Map.h
@@ -76,6 +76,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -479,6 +481,7 @@ class Map : public GridRefManager<NGridType>
void LoadMapAndVMap(int gx, int gy);
void LoadVMap(int gx, int gy);
void LoadMap(int gx, int gy, bool reload = false);
+ void LoadMMap(int gx, int gy);
GridMap* GetGrid(float x, float y);
void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp
index 5fcca05361b..e0dbc19aff4 100755
--- a/src/server/game/Maps/MapInstanced.cpp
+++ b/src/server/game/Maps/MapInstanced.cpp
@@ -21,6 +21,7 @@
#include "MapManager.h"
#include "Battleground.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "InstanceSaveMgr.h"
#include "World.h"
#include "Group.h"
@@ -260,6 +261,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
if (m_InstancedMaps.size() <= 1 && sWorld->getBoolConfig(CONFIG_GRID_UNLOAD))
{
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId());
+ MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(itr->second->GetId());
// in that case, unload grids of the base map, too
// so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded
Map::UnloadAll();
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index f74d6c44be7..501408243bb 100755
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -19,6 +19,7 @@
#ifndef TRINITY_SHAREDDEFINES_H
#define TRINITY_SHAREDDEFINES_H
+#include "DetourNavMesh.h"
#include "Define.h"
#include <cassert>
@@ -3522,4 +3523,33 @@ enum CalendarError
// Calendar - end
+#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
+#define MMAP_VERSION 3
+
+struct MmapTileHeader
+{
+ uint32 mmapMagic;
+ uint32 dtVersion;
+ uint32 mmapVersion;
+ uint32 size;
+ bool usesLiquids : 1;
+
+ MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION),
+ mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {}
+};
+
+enum NavTerrain
+{
+ NAV_EMPTY = 0x00,
+ NAV_GROUND = 0x01,
+ NAV_MAGMA = 0x02,
+ NAV_SLIME = 0x04,
+ NAV_WATER = 0x08,
+ NAV_UNUSED1 = 0x10,
+ NAV_UNUSED2 = 0x20,
+ NAV_UNUSED3 = 0x40,
+ NAV_UNUSED4 = 0x80
+ // we only have 8 bits
+};
+
#endif
diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp
index 26e83773228..2ad8028ca4f 100755..100644
--- a/src/server/game/Movement/MotionMaster.cpp
+++ b/src/server/game/Movement/MotionMaster.cpp
@@ -86,7 +86,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
ASSERT(!empty());
_cleanFlag |= MMCF_UPDATE;
- if (!top()->Update(*_owner, diff))
+ if (!top()->Update(_owner, diff))
{
_cleanFlag &= ~MMCF_UPDATE;
MovementExpired();
@@ -110,7 +110,7 @@ void MotionMaster::UpdateMotion(uint32 diff)
else if (needInitTop())
InitTop();
else if (_cleanFlag & MMCF_RESET)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
_cleanFlag &= ~MMCF_RESET;
}
@@ -131,7 +131,7 @@ void MotionMaster::DirectClean(bool reset)
if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedClean()
@@ -162,7 +162,7 @@ void MotionMaster::DirectExpire(bool reset)
else if (needInitTop())
InitTop();
else if (reset)
- top()->Reset(*_owner);
+ top()->Reset(_owner);
}
void MotionMaster::DelayedExpire()
@@ -198,19 +198,19 @@ void MotionMaster::MoveTargetedHome()
{
Clear(false);
- if (_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ if (_owner->GetTypeId() == TYPEID_UNIT && !_owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted home", _owner->GetEntry(), _owner->GetGUIDLow());
Mutate(new HomeMovementGenerator<Creature>(), MOTION_SLOT_ACTIVE);
}
- else if (_owner->GetTypeId()==TYPEID_UNIT && ((Creature*)_owner)->GetCharmerOrOwnerGUID())
+ else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID())
{
sLog->outDebug(LOG_FILTER_GENERAL, "Pet or controlled creature (Entry: %u GUID: %u) targeting home", _owner->GetEntry(), _owner->GetGUIDLow());
- Unit *target = ((Creature*)_owner)->GetCharmerOrOwner();
+ Unit* target = _owner->ToCreature()->GetCharmerOrOwner();
if (target)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : ((Creature*)target)->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
+ Mutate(new FollowMovementGenerator<Creature>(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE);
}
}
else
@@ -247,7 +247,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Player>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Player>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
else
{
@@ -255,7 +255,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle)
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new ChaseMovementGenerator<Creature>(*target,dist,angle), MOTION_SLOT_ACTIVE);
+ Mutate(new ChaseMovementGenerator<Creature>(target, dist, angle), MOTION_SLOT_ACTIVE);
}
}
@@ -271,7 +271,7 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) follow to %s (GUID: %u)", _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Player>(*target,dist,angle), slot);
+ Mutate(new FollowMovementGenerator<Player>(target, dist, angle), slot);
}
else
{
@@ -279,22 +279,22 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo
_owner->GetEntry(), _owner->GetGUIDLow(),
target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature",
target->GetTypeId() == TYPEID_PLAYER ? target->GetGUIDLow() : target->ToCreature()->GetDBTableGUIDLow());
- Mutate(new FollowMovementGenerator<Creature>(*target,dist,angle), slot);
+ Mutate(new FollowMovementGenerator<Creature>(target, dist, angle), slot);
}
}
-void MotionMaster::MovePoint(uint32 id, float x, float y, float z)
+void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath)
{
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), id, x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z), MOTION_SLOT_ACTIVE);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE);
}
}
@@ -305,7 +305,7 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x,y,z);
init.SetAnimation(Movement::ToGround);
init.Launch();
@@ -319,7 +319,7 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos)
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x,y,z);
init.SetAnimation(Movement::ToFly);
init.Launch();
@@ -339,7 +339,7 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa
_owner->GetNearPoint(_owner, x, y, z, _owner->GetObjectSize(), dist, _owner->GetAngle(srcX, srcY) + M_PI);
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(x,y,z);
init.SetParabolic(max_height,0);
init.SetOrientationFixed(true);
@@ -369,8 +369,8 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee
float moveTimeHalf = speedZ / Movement::gravity;
float max_height = -Movement::computeFallElevation(moveTimeHalf,false,-speedZ);
- Movement::MoveSplineInit init(*_owner);
- init.MoveTo(x,y,z);
+ Movement::MoveSplineInit init(_owner);
+ init.MoveTo(x, y, z, false);
init.SetParabolic(max_height,0);
init.SetVelocity(speedXY);
init.Launch();
@@ -398,14 +398,14 @@ void MotionMaster::MoveFall(uint32 id/*=0*/)
_owner->m_movementInfo.SetFallTime(0);
}
- Movement::MoveSplineInit init(*_owner);
+ Movement::MoveSplineInit init(_owner);
init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz);
init.SetFall();
init.Launch();
Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED);
}
-void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
+void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, bool generatePath)
{
if (Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
@@ -413,13 +413,13 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
if (_owner->GetTypeId() == TYPEID_PLAYER)
{
sLog->outDebug(LOG_FILTER_GENERAL, "Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Player>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Player>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
else
{
sLog->outDebug(LOG_FILTER_GENERAL, "Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)",
_owner->GetEntry(), _owner->GetGUIDLow(), x, y, z);
- Mutate(new PointMovementGenerator<Creature>(id, x, y, z, speed), MOTION_SLOT_CONTROLLED);
+ Mutate(new PointMovementGenerator<Creature>(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED);
}
}
@@ -545,7 +545,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
else
{
_needInit[slot] = false;
- m->Initialize(*_owner);
+ m->Initialize(_owner);
}
}
@@ -613,7 +613,7 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
void MotionMaster::InitTop()
{
- top()->Initialize(*_owner);
+ top()->Initialize(_owner);
_needInit[_top] = false;
}
@@ -621,7 +621,7 @@ void MotionMaster::DirectDelete(_Ty curr)
{
if (isStatic(curr))
return;
- curr->Finalize(*_owner);
+ curr->Finalize(_owner);
delete curr;
}
diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h
index 727f626cdea..16accff2ab4 100755
--- a/src/server/game/Movement/MotionMaster.h
+++ b/src/server/game/Movement/MotionMaster.h
@@ -154,13 +154,13 @@ class MotionMaster //: private std::stack<MovementGenerator *>
void MoveFleeing(Unit* enemy, uint32 time = 0);
void MovePoint(uint32 id, const Position &pos)
{ MovePoint(id, pos.m_positionX, pos.m_positionY, pos.m_positionZ); }
- void MovePoint(uint32 id, float x, float y, float z);
+ void MovePoint(uint32 id, float x, float y, float z, bool generatePath = true);
// These two movement types should only be used with creatures having landing/takeoff animations
void MoveLand(uint32 id, Position const& pos);
void MoveTakeoff(uint32 id, Position const& pos);
- void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE);
+ void MoveCharge(float x, float y, float z, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE, bool generatePath = false);
void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ);
void MoveJumpTo(float angle, float speedXY, float speedZ);
void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = 0);
diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h
index 0a2ebcfaeee..ee8dfb51cce 100755
--- a/src/server/game/Movement/MovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerator.h
@@ -33,48 +33,54 @@ class MovementGenerator
public:
virtual ~MovementGenerator();
- virtual void Initialize(Unit &) = 0;
- virtual void Finalize(Unit &) = 0;
+ virtual void Initialize(Unit*) = 0;
+ virtual void Finalize(Unit*) = 0;
- virtual void Reset(Unit &) = 0;
+ virtual void Reset(Unit*) = 0;
- virtual bool Update(Unit &, const uint32& time_diff) = 0;
+ virtual bool Update(Unit*, const uint32& time_diff) = 0;
virtual MovementGeneratorType GetMovementGeneratorType() = 0;
virtual void unitSpeedChanged() { }
+
+ // used by Evade code for select point to evade with expected restart default movement
+ virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; }
};
template<class T, class D>
class MovementGeneratorMedium : public MovementGenerator
{
public:
- void Initialize(Unit &u)
+ void Initialize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->Initialize(*((T*)&u));
+ (static_cast<D*>(this))->Initialize(static_cast<T*>(u));
}
- void Finalize(Unit &u)
+
+ void Finalize(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->Finalize(*((T*)&u));
+ (static_cast<D*>(this))->Finalize(static_cast<T*>(u));
}
- void Reset(Unit &u)
+
+ void Reset(Unit* u)
{
//u->AssertIsType<T>();
- (static_cast<D*>(this))->Reset(*((T*)&u));
+ (static_cast<D*>(this))->Reset(static_cast<T*>(u));
}
- bool Update(Unit &u, const uint32& time_diff)
+
+ bool Update(Unit* u, const uint32& time_diff)
{
//u->AssertIsType<T>();
- return (static_cast<D*>(this))->Update(*((T*)&u), time_diff);
+ return (static_cast<D*>(this))->Update(static_cast<T*>(u), time_diff);
}
public:
// will not link if not overridden in the generators
- void Initialize(T &u);
- void Finalize(T &u);
- void Reset(T &u);
- bool Update(T &u, const uint32& time_diff);
+ void Initialize(T* u);
+ void Finalize(T* u);
+ void Reset(T* u);
+ bool Update(T* u, const uint32& time_diff);
};
struct SelectableMovement : public FactoryHolder<MovementGenerator, MovementGeneratorType>
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
index 54a68f92c66..f1d1d8cf04a 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp
@@ -19,6 +19,7 @@
#include "Creature.h"
#include "MapManager.h"
#include "ConfusedMovementGenerator.h"
+#include "PathGenerator.h"
#include "VMapFactory.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -30,99 +31,35 @@
#endif
template<class T>
-void ConfusedMovementGenerator<T>::Initialize(T &unit)
+void ConfusedMovementGenerator<T>::Initialize(T* unit)
{
- float const wander_distance = 4;
- float x = unit.GetPositionX();
- float y = unit.GetPositionY();
- float z = unit.GetPositionZ();
-
- Map const* map = unit.GetBaseMap();
-
- i_nextMove = 1;
-
- bool is_water_ok, is_land_ok;
- _InitSpecific(unit, is_water_ok, is_land_ok);
-
- for (uint8 idx = 0; idx < MAX_CONF_WAYPOINTS + 1; ++idx)
- {
- float wanderX = x + (wander_distance * (float)rand_norm() - wander_distance/2);
- float wanderY = y + (wander_distance * (float)rand_norm() - wander_distance/2);
-
- // prevent invalid coordinates generation
- Trinity::NormalizeMapCoord(wanderX);
- Trinity::NormalizeMapCoord(wanderY);
-
- if (unit.IsWithinLOS(wanderX, wanderY, z))
- {
- bool is_water = map->IsInWater(wanderX, wanderY, z);
-
- if ((is_water && !is_water_ok) || (!is_water && !is_land_ok))
- {
- //! Cannot use coordinates outside our InhabitType. Use the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
- }
- else
- {
- //! Trying to access path outside line of sight. Skip this by using the current or previous position.
- wanderX = idx > 0 ? i_waypoints[idx-1][0] : x;
- wanderY = idx > 0 ? i_waypoints[idx-1][1] : y;
- }
-
- unit.UpdateAllowedPositionZ(wanderX, wanderY, z);
-
- //! Positions are fine - apply them to this waypoint
- i_waypoints[idx][0] = wanderX;
- i_waypoints[idx][1] = wanderY;
- i_waypoints[idx][2] = z;
- }
-
- unit.StopMoving();
- unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
-}
-
-template<>
-void ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = creature.canSwim();
- is_land_ok = creature.canWalk();
-}
-
-template<>
-void ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
-{
- is_water_ok = true;
- is_land_ok = true;
+ unit->GetPosition(i_x, i_y, i_z);
+ unit->StopMoving();
+ unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
}
template<class T>
-void ConfusedMovementGenerator<T>::Reset(T &unit)
+void ConfusedMovementGenerator<T>::Reset(T* unit)
{
- i_nextMove = 1;
i_nextMoveTime.Reset(0);
- unit.StopMoving();
- unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->StopMoving();
+ unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
}
template<class T>
-bool ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+bool ConfusedMovementGenerator<T>::Update(T* unit, const uint32& diff)
{
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return true;
if (i_nextMoveTime.Passed())
{
// currently moving, update location
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
- if (unit.movespline->Finalized())
- {
- i_nextMove = urand(1, MAX_CONF_WAYPOINTS);
- i_nextMoveTime.Reset(urand(500, 1200)); // Guessed
- }
+ if (unit->movespline->Finalized())
+ i_nextMoveTime.Reset(urand(800, 1500));
}
else
{
@@ -131,14 +68,25 @@ bool ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
if (i_nextMoveTime.Passed())
{
// start moving
- unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+ unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE);
+
+ float x = i_x + 10.0f*((float)rand_norm() - 0.5f);
+ float y = i_y + 10.0f*((float)rand_norm() - 0.5f);
+ float z = i_z;
+
+ unit->UpdateAllowedPositionZ(x, y, z);
+
+ PathGenerator path(unit);
+ path.setPathLengthLimit(30.0f);
+ path.CalculatePath(x, y, z);
+ if (path.getPathType() & PATHFIND_NOPATH)
+ {
+ i_nextMoveTime.Reset(urand(800, 1000));
+ return true;
+ }
- ASSERT(i_nextMove <= MAX_CONF_WAYPOINTS);
- float x = i_waypoints[i_nextMove][0];
- float y = i_waypoints[i_nextMove][1];
- float z = i_waypoints[i_nextMove][2];
Movement::MoveSplineInit init(unit);
- init.MoveTo(x, y, z);
+ init.MovebyPath(path.getPath());
init.SetWalk(true);
init.Launch();
}
@@ -148,25 +96,26 @@ bool ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
}
template<>
-void ConfusedMovementGenerator<Player>::Finalize(Player &unit)
+void ConfusedMovementGenerator<Player>::Finalize(Player* unit)
{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ unit->StopMoving();
}
template<>
-void ConfusedMovementGenerator<Creature>::Finalize(Creature &unit)
+void ConfusedMovementGenerator<Creature>::Finalize(Creature* unit)
{
- unit.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
- unit.ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
- if (unit.getVictim())
- unit.SetTarget(unit.getVictim()->GetGUID());
+ unit->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
+ unit->ClearUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE);
+ if (unit->getVictim())
+ unit->SetTarget(unit->getVictim()->GetGUID());
}
-template void ConfusedMovementGenerator<Player>::Initialize(Player &player);
-template void ConfusedMovementGenerator<Creature>::Initialize(Creature &creature);
-template void ConfusedMovementGenerator<Player>::Reset(Player &player);
-template void ConfusedMovementGenerator<Creature>::Reset(Creature &creature);
-template bool ConfusedMovementGenerator<Player>::Update(Player &player, const uint32 &diff);
-template bool ConfusedMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff);
+template void ConfusedMovementGenerator<Player>::Initialize(Player*);
+template void ConfusedMovementGenerator<Creature>::Initialize(Creature*);
+template void ConfusedMovementGenerator<Player>::Reset(Player*);
+template void ConfusedMovementGenerator<Creature>::Reset(Creature*);
+template bool ConfusedMovementGenerator<Player>::Update(Player*, const uint32&);
+template bool ConfusedMovementGenerator<Creature>::Update(Creature*, const uint32&);
diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
index 7f2226ea069..5b535e2d47d 100755
--- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h
@@ -22,25 +22,21 @@
#include "MovementGenerator.h"
#include "Timer.h"
-#define MAX_CONF_WAYPOINTS 24 //! Allows a twelve second confusion if i_nextMove always is the absolute minimum timer.
-
template<class T>
class ConfusedMovementGenerator : public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
public:
explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
- void Initialize(T &);
- void Finalize(T &);
- void Reset(T &);
- bool Update(T &, const uint32 &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, const uint32&);
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
- void _InitSpecific(T &, bool &, bool &);
TimeTracker i_nextMoveTime;
- float i_waypoints[MAX_CONF_WAYPOINTS+1][3];
- uint32 i_nextMove;
+ float i_x, i_y, i_z;
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
index 7e130a2c143..acb1a57a9ba 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp
@@ -20,6 +20,7 @@
#include "CreatureAI.h"
#include "MapManager.h"
#include "FleeingMovementGenerator.h"
+#include "PathGenerator.h"
#include "ObjectAccessor.h"
#include "MoveSplineInit.h"
#include "MoveSpline.h"
@@ -28,378 +29,175 @@
#define MAX_QUIET_DISTANCE 43.0f
template<class T>
-void FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
+void FleeingMovementGenerator<T>::_setTargetLocation(T* owner)
{
- if (!&owner)
+ if (!owner)
return;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
- return;
-
- if (!_setMoveData(owner))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
return;
float x, y, z;
if (!_getPoint(owner, x, y, z))
return;
- owner.AddUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->AddUnitState(UNIT_STATE_FLEEING_MOVE);
+
+ PathGenerator path(owner);
+ path.setPathLengthLimit(30.0f);
+ path.CalculatePath(x, y, z);
+ if (path.getPathType() & PATHFIND_NOPATH)
+ {
+ i_nextCheckTime.Reset(urand(1000, 1500));
+ return;
+ }
Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
+ init.MovebyPath(path.getPath());
init.SetWalk(false);
- init.Launch();
+ int32 traveltime = init.Launch();
+ i_nextCheckTime.Reset(traveltime + urand(800, 1500));
}
template<class T>
-bool FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
+bool FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z)
{
- if (!&owner)
+ if (!owner)
return false;
- x = owner.GetPositionX();
- y = owner.GetPositionY();
- z = owner.GetPositionZ();
-
- float temp_x, temp_y, angle;
- const Map* _map = owner.GetBaseMap();
- // primitive path-finding
- for (uint8 i = 0; i < 18; ++i)
- {
- if (i_only_forward && i > 2)
- break;
-
- float distance = 5.0f;
-
- switch (i)
- {
- case 0:
- angle = i_cur_angle;
- break;
- case 1:
- angle = i_cur_angle;
- distance /= 2;
- break;
- case 2:
- angle = i_cur_angle;
- distance /= 4;
- break;
- case 3:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- break;
- case 4:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- break;
- case 5:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 6:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 2;
- break;
- case 7:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- break;
- case 8:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- break;
- case 9:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 10:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 2;
- break;
- case 11:
- angle = i_cur_angle + static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 12:
- angle = i_cur_angle - static_cast<float>(M_PI/4);
- distance /= 4;
- break;
- case 13:
- angle = i_cur_angle + static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 14:
- angle = i_cur_angle - static_cast<float>(M_PI/2);
- distance /= 4;
- break;
- case 15:
- angle = i_cur_angle + static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 16:
- angle = i_cur_angle - static_cast<float>(3*M_PI/4);
- distance /= 2;
- break;
- case 17:
- angle = i_cur_angle + static_cast<float>(M_PI);
- distance /= 2;
- break;
- }
- temp_x = x + distance * cos(angle);
- temp_y = y + distance * sin(angle);
- Trinity::NormalizeMapCoord(temp_x);
- Trinity::NormalizeMapCoord(temp_y);
- if (owner.IsWithinLOS(temp_x, temp_y, z))
- {
- bool is_water_now = _map->IsInWater(x,y,z);
-
- if (is_water_now && _map->IsInWater(temp_x,temp_y,z))
- {
- x = temp_x;
- y = temp_y;
- return true;
- }
- float new_z = _map->GetHeight(owner.GetPhaseMask(), temp_x, temp_y, z, true);
-
- if (new_z <= INVALID_HEIGHT)
- continue;
-
- bool is_water_next = _map->IsInWater(temp_x, temp_y, new_z);
-
- if ((is_water_now && !is_water_next && !is_land_ok) || (!is_water_now && is_water_next && !is_water_ok))
- continue;
-
- if (!(new_z - z) || distance / fabs(new_z - z) > 1.0f)
- {
- float new_z_left = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f*cos(angle+static_cast<float>(M_PI/2)),temp_y + 1.0f*sin(angle+static_cast<float>(M_PI/2)),z,true);
- float new_z_right = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f*cos(angle-static_cast<float>(M_PI/2)),temp_y + 1.0f*sin(angle-static_cast<float>(M_PI/2)),z,true);
- if (fabs(new_z_left - new_z) < 1.2f && fabs(new_z_right - new_z) < 1.2f)
- {
- x = temp_x;
- y = temp_y;
- z = new_z;
- return true;
- }
- }
- }
- }
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500,1000));
- return false;
-}
-
-template<class T>
-bool FleeingMovementGenerator<T>::_setMoveData(T &owner)
-{
- float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
-
- if (i_to_distance_from_caster > 0.0f)
- {
- if ((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
- // if we reach lower distance
- (i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz > i_last_distance_from_caster) ||
- // if we can't be close
- (i_last_distance_from_caster < i_to_distance_from_caster && cur_dist_xyz > i_to_distance_from_caster) ||
- // if we reach bigger distance
- (cur_dist_xyz > MAX_QUIET_DISTANCE) || // if we are too far
- (i_last_distance_from_caster > MIN_QUIET_DISTANCE && cur_dist_xyz < MIN_QUIET_DISTANCE))
- // if we leave 'quiet zone'
- {
- // we are very far or too close, stopping
- i_to_distance_from_caster = 0.0f;
- i_nextCheckTime.Reset(urand(500,1000));
- return false;
- }
- else
- {
- // now we are running, continue
- i_last_distance_from_caster = cur_dist_xyz;
- return true;
- }
- }
-
- float cur_dist;
- float angle_to_caster;
-
- if (Unit* fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
+ float dist_from_caster, angle_to_caster;
+ if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID))
{
- cur_dist = fright->GetDistance(&owner);
- if (cur_dist < cur_dist_xyz)
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- angle_to_caster = fright->GetAngle(&owner);
- }
+ dist_from_caster = fright->GetDistance(owner);
+ if (dist_from_caster > 0.2f)
+ angle_to_caster = fright->GetAngle(owner);
else
- {
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
- }
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
}
else
{
- cur_dist = cur_dist_xyz;
- angle_to_caster = owner.GetAngle(i_caster_x, i_caster_y) + static_cast<float>(M_PI);
+ dist_from_caster = 0.0f;
+ angle_to_caster = frand(0, 2 * static_cast<float>(M_PI));
}
- // if we too close may use 'path-finding' else just stop
- i_only_forward = cur_dist >= MIN_QUIET_DISTANCE/3;
-
- //get angle and 'distance from caster' to run
- float angle;
-
- if (i_cur_angle == 0.0f && i_last_distance_from_caster == 0.0f) //just started, first time
+ float dist, angle;
+ if (dist_from_caster < MIN_QUIET_DISTANCE)
{
- angle = (float)rand_norm()*(1.0f - cur_dist/MIN_QUIET_DISTANCE) * static_cast<float>(M_PI/3) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE;
- i_only_forward = true;
+ dist = frand(0.4f, 1.3f)*(MIN_QUIET_DISTANCE - dist_from_caster);
+ angle = angle_to_caster + frand(-static_cast<float>(M_PI)/8, static_cast<float>(M_PI)/8);
}
- else if (cur_dist < MIN_QUIET_DISTANCE)
+ else if(dist_from_caster > MAX_QUIET_DISTANCE)
{
- angle = static_cast<float>(M_PI/6) + (float)rand_norm()*static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = cur_dist*2/3 + (float)rand_norm()*(MIN_QUIET_DISTANCE - cur_dist*2/3);
+ dist = frand(0.4f, 1.0f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = -angle_to_caster + frand(-static_cast<float>(M_PI)/4, static_cast<float>(M_PI)/4);
}
- else if (cur_dist > MAX_QUIET_DISTANCE)
+ else // we are inside quiet range
{
- angle = (float)rand_norm()*static_cast<float>(M_PI/3) + static_cast<float>(M_PI*2/3);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
- }
- else
- {
- angle = (float)rand_norm()*static_cast<float>(M_PI);
- i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + (float)rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
+ dist = frand(0.6f, 1.2f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE);
+ angle = frand(0, 2*static_cast<float>(M_PI));
}
- int8 sign = (float)rand_norm() > 0.5f ? 1 : -1;
- i_cur_angle = sign*angle + angle_to_caster;
+ float curr_x, curr_y, curr_z;
+ owner->GetPosition(curr_x, curr_y, curr_z);
+
+ x = curr_x + dist*cos(angle);
+ y = curr_y + dist*sin(angle);
+ z = curr_z;
- // current distance
- i_last_distance_from_caster = cur_dist;
+ owner->UpdateAllowedPositionZ(x, y, z);
return true;
}
template<class T>
-void FleeingMovementGenerator<T>::Initialize(T &owner)
+void FleeingMovementGenerator<T>::Initialize(T* owner)
{
- if (!&owner)
+ if (!owner)
return;
- owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
-
- _Init(owner);
-
- if (Unit *fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
- {
- i_caster_x = fright->GetPositionX();
- i_caster_y = fright->GetPositionY();
- i_caster_z = fright->GetPositionZ();
- }
- else
- {
- i_caster_x = owner.GetPositionX();
- i_caster_y = owner.GetPositionY();
- i_caster_z = owner.GetPositionZ();
- }
-
- i_only_forward = true;
- i_cur_angle = 0.0f;
- i_last_distance_from_caster = 0.0f;
- i_to_distance_from_caster = 0.0f;
- _setTargetLocation(owner);
-}
+ owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->AddUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
-template<>
-void FleeingMovementGenerator<Creature>::_Init(Creature &owner)
-{
- if (!&owner)
+ if (owner->GetTypeId() == TYPEID_UNIT)
return;
- //owner.SetTargetGuid(ObjectGuid());
- is_water_ok = owner.canSwim();
- is_land_ok = owner.canWalk();
-}
-
-template<>
-void FleeingMovementGenerator<Player>::_Init(Player &)
-{
- is_water_ok = true;
- is_land_ok = true;
+ _setTargetLocation(owner);
}
template<>
-void FleeingMovementGenerator<Player>::Finalize(Player &owner)
+void FleeingMovementGenerator<Player>::Finalize(Player* owner)
{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE);
+ owner->StopMoving();
}
template<>
-void FleeingMovementGenerator<Creature>::Finalize(Creature &owner)
+void FleeingMovementGenerator<Creature>::Finalize(Creature* owner)
{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (owner.getVictim())
- owner.SetTarget(owner.getVictim()->GetGUID());
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (owner->getVictim())
+ owner->SetTarget(owner->getVictim()->GetGUID());
}
template<class T>
-void FleeingMovementGenerator<T>::Reset(T &owner)
+void FleeingMovementGenerator<T>::Reset(T* owner)
{
Initialize(owner);
}
template<class T>
-bool FleeingMovementGenerator<T>::Update(T &owner, const uint32 &time_diff)
+bool FleeingMovementGenerator<T>::Update(T* owner, const uint32& time_diff)
{
- if (!&owner || !owner.isAlive())
+ if (!owner || !owner->isAlive())
return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
i_nextCheckTime.Update(time_diff);
- if (i_nextCheckTime.Passed() && owner.movespline->Finalized())
+ if (i_nextCheckTime.Passed() && owner->movespline->Finalized())
_setTargetLocation(owner);
return true;
}
-template void FleeingMovementGenerator<Player>::Initialize(Player &);
-template void FleeingMovementGenerator<Creature>::Initialize(Creature &);
-template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
-template bool FleeingMovementGenerator<Creature>::_setMoveData(Creature &);
-template bool FleeingMovementGenerator<Player>::_getPoint(Player &, float &, float &, float &);
-template bool FleeingMovementGenerator<Creature>::_getPoint(Creature &, float &, float &, float &);
-template void FleeingMovementGenerator<Player>::_setTargetLocation(Player &);
-template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature &);
-template void FleeingMovementGenerator<Player>::Reset(Player &);
-template void FleeingMovementGenerator<Creature>::Reset(Creature &);
-template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &);
-template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &);
-
-void TimedFleeingMovementGenerator::Finalize(Unit &owner)
+template void FleeingMovementGenerator<Player>::Initialize(Player*);
+template void FleeingMovementGenerator<Creature>::Initialize(Creature*);
+template bool FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&);
+template bool FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&);
+template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*);
+template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*);
+template void FleeingMovementGenerator<Player>::Reset(Player*);
+template void FleeingMovementGenerator<Creature>::Reset(Creature*);
+template bool FleeingMovementGenerator<Player>::Update(Player*, const uint32&);
+template bool FleeingMovementGenerator<Creature>::Update(Creature*, const uint32&);
+
+void TimedFleeingMovementGenerator::Finalize(Unit* owner)
{
- owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
- owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
- if (Unit* victim = owner.getVictim())
+ owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
+ owner->ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE);
+ if (Unit* victim = owner->getVictim())
{
- if (owner.isAlive())
+ if (owner->isAlive())
{
- owner.AttackStop();
- owner.ToCreature()->AI()->AttackStart(victim);
+ owner->AttackStop();
+ owner->ToCreature()->AI()->AttackStart(victim);
}
}
}
-bool TimedFleeingMovementGenerator::Update(Unit & owner, const uint32& time_diff)
+bool TimedFleeingMovementGenerator::Update(Unit* owner, const uint32& time_diff)
{
- if (!owner.isAlive())
+ if (!owner->isAlive())
return false;
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- owner.ClearUnitState(UNIT_STATE_FLEEING_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FLEEING_MOVE);
return true;
}
diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
index aec93ad3375..88dedd31af6 100755
--- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h
@@ -27,29 +27,17 @@ class FleeingMovementGenerator : public MovementGeneratorMedium< T, FleeingMovem
public:
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
- void Initialize(T &);
- void Finalize(T &);
- void Reset(T &);
- bool Update(T &, const uint32 &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, const uint32&);
MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; }
private:
- void _setTargetLocation(T &owner);
- bool _getPoint(T &owner, float &x, float &y, float &z);
- bool _setMoveData(T &owner);
- void _Init(T &);
+ void _setTargetLocation(T*);
+ bool _getPoint(T*, float &x, float &y, float &z);
- bool is_water_ok :1;
- bool is_land_ok :1;
- bool i_only_forward:1;
-
- float i_caster_x;
- float i_caster_y;
- float i_caster_z;
- float i_last_distance_from_caster;
- float i_to_distance_from_caster;
- float i_cur_angle;
uint64 i_frightGUID;
TimeTracker i_nextCheckTime;
};
@@ -62,8 +50,8 @@ class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature>
i_totalFleeTime(time) {}
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
- bool Update(Unit &, const uint32&);
- void Finalize(Unit &);
+ bool Update(Unit*, const uint32&);
+ void Finalize(Unit*);
private:
TimeTracker i_totalFleeTime;
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
index a8bdb698432..78c1f80005c 100755..100644
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp
@@ -23,49 +23,49 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void HomeMovementGenerator<Creature>::Initialize(Creature & owner)
+void HomeMovementGenerator<Creature>::Initialize(Creature* owner)
{
_setTargetLocation(owner);
}
-void HomeMovementGenerator<Creature>::Reset(Creature &)
+void HomeMovementGenerator<Creature>::Finalize(Creature* owner)
{
+ if (arrived)
+ {
+ owner->ClearUnitState(UNIT_STATE_EVADE);
+ owner->SetWalk(true);
+ owner->LoadCreaturesAddon(true);
+ owner->AI()->JustReachedHome();
+ }
}
-void HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
+void HomeMovementGenerator<Creature>::Reset(Creature*)
{
- if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+}
+
+void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner)
+{
+ if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
return;
Movement::MoveSplineInit init(owner);
float x, y, z, o;
// at apply we can select more nice return points base at current movegen
- //if (owner.GetMotionMaster()->empty() || !owner.GetMotionMaster()->top()->GetResetPosition(owner,x,y,z))
- //{
- owner.GetHomePosition(x, y, z, o);
- init.SetFacing(o);
- //}
- init.MoveTo(x,y,z);
+ if (owner->GetMotionMaster()->empty() || !owner->GetMotionMaster()->top()->GetResetPosition(owner, x, y, z))
+ {
+ owner->GetHomePosition(x, y, z, o);
+ init.SetFacing(o);
+ }
+ init.MoveTo(x, y, z);
init.SetWalk(false);
init.Launch();
arrived = false;
- owner.ClearUnitState(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE);
+ owner->ClearUnitState(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE);
}
-bool HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32 /*time_diff*/)
+bool HomeMovementGenerator<Creature>::Update(Creature* owner, const uint32 /*time_diff*/)
{
- arrived = owner.movespline->Finalized();
+ arrived = owner->movespline->Finalized();
return !arrived;
}
-
-void HomeMovementGenerator<Creature>::Finalize(Creature& owner)
-{
- if (arrived)
- {
- owner.ClearUnitState(UNIT_STATE_EVADE);
- owner.SetWalk(true);
- owner.LoadCreaturesAddon(true);
- owner.AI()->JustReachedHome();
- }
-}
diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
index 95eb05f281c..dfbea60d353 100755
--- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h
@@ -34,14 +34,14 @@ class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature
HomeMovementGenerator() : arrived(false) {}
~HomeMovementGenerator() {}
- void Initialize(Creature &);
- void Finalize(Creature &);
- void Reset(Creature &);
- bool Update(Creature &, const uint32);
+ void Initialize(Creature*);
+ void Finalize(Creature*);
+ void Reset(Creature*);
+ bool Update(Creature*, const uint32);
MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; }
private:
- void _setTargetLocation(Creature &);
+ void _setTargetLocation(Creature*);
bool arrived;
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
index 5a2090cfe28..8ef9bd7697a 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
@@ -24,33 +24,33 @@ IdleMovementGenerator si_idleMovement;
// StopMoving is needed to make unit stop if its last movement generator expires
// But it should not be sent otherwise there are many redundent packets
-void IdleMovementGenerator::Initialize(Unit &owner)
+void IdleMovementGenerator::Initialize(Unit* owner)
{
Reset(owner);
}
-void IdleMovementGenerator::Reset(Unit& owner)
+void IdleMovementGenerator::Reset(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
}
-void RotateMovementGenerator::Initialize(Unit& owner)
+void RotateMovementGenerator::Initialize(Unit* owner)
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
- if (owner.getVictim())
- owner.SetInFront(owner.getVictim());
+ if (owner->getVictim())
+ owner->SetInFront(owner->getVictim());
- owner.AddUnitState(UNIT_STATE_ROTATING);
+ owner->AddUnitState(UNIT_STATE_ROTATING);
- owner.AttackStop();
+ owner->AttackStop();
}
-bool RotateMovementGenerator::Update(Unit& owner, const uint32& diff)
+bool RotateMovementGenerator::Update(Unit* owner, const uint32& diff)
{
- float angle = owner.GetOrientation();
+ float angle = owner->GetOrientation();
if (m_direction == ROTATE_DIRECTION_LEFT)
{
angle += (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
@@ -61,8 +61,8 @@ bool RotateMovementGenerator::Update(Unit& owner, const uint32& diff)
angle -= (float)diff * static_cast<float>(M_PI * 2) / m_maxDuration;
while (angle < 0) angle += static_cast<float>(M_PI * 2);
}
- owner.SetOrientation(angle);
- owner.SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
+ owner->SetOrientation(angle);
+ owner->SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
if (m_duration > diff)
m_duration -= diff;
@@ -72,24 +72,24 @@ bool RotateMovementGenerator::Update(Unit& owner, const uint32& diff)
return true;
}
-void RotateMovementGenerator::Finalize(Unit &unit)
+void RotateMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROTATING);
- if (unit.GetTypeId() == TYPEID_UNIT)
- unit.ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
+ unit->ClearUnitState(UNIT_STATE_ROTATING);
+ if (unit->GetTypeId() == TYPEID_UNIT)
+ unit->ToCreature()->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
}
-void DistractMovementGenerator::Initialize(Unit& owner)
+void DistractMovementGenerator::Initialize(Unit* owner)
{
- owner.AddUnitState(UNIT_STATE_DISTRACTED);
+ owner->AddUnitState(UNIT_STATE_DISTRACTED);
}
-void DistractMovementGenerator::Finalize(Unit& owner)
+void DistractMovementGenerator::Finalize(Unit* owner)
{
- owner.ClearUnitState(UNIT_STATE_DISTRACTED);
+ owner->ClearUnitState(UNIT_STATE_DISTRACTED);
}
-bool DistractMovementGenerator::Update(Unit& /*owner*/, const uint32& time_diff)
+bool DistractMovementGenerator::Update(Unit* /*owner*/, const uint32& time_diff)
{
if (time_diff > m_timer)
return false;
@@ -98,9 +98,9 @@ bool DistractMovementGenerator::Update(Unit& /*owner*/, const uint32& time_diff)
return true;
}
-void AssistanceDistractMovementGenerator::Finalize(Unit &unit)
+void AssistanceDistractMovementGenerator::Finalize(Unit* unit)
{
- unit.ClearUnitState(UNIT_STATE_DISTRACTED);
- unit.ToCreature()->SetReactState(REACT_AGGRESSIVE);
+ unit->ClearUnitState(UNIT_STATE_DISTRACTED);
+ unit->ToCreature()->SetReactState(REACT_AGGRESSIVE);
}
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
index 7acec82af98..14287d871c9 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.h
@@ -25,10 +25,10 @@ class IdleMovementGenerator : public MovementGenerator
{
public:
- void Initialize(Unit &);
- void Finalize(Unit &) { }
- void Reset(Unit &);
- bool Update(Unit &, const uint32&) { return true; }
+ void Initialize(Unit*);
+ void Finalize(Unit*) { }
+ void Reset(Unit*);
+ bool Update(Unit*, const uint32&) { return true; }
MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; }
};
@@ -39,10 +39,10 @@ class RotateMovementGenerator : public MovementGenerator
public:
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, const uint32& time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, const uint32&);
MovementGeneratorType GetMovementGeneratorType() { return ROTATE_MOTION_TYPE; }
private:
@@ -55,10 +55,10 @@ class DistractMovementGenerator : public MovementGenerator
public:
explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {}
- void Initialize(Unit& owner);
- void Finalize(Unit& owner);
- void Reset(Unit& owner) { Initialize(owner); }
- bool Update(Unit& owner, const uint32& time_diff);
+ void Initialize(Unit*);
+ void Finalize(Unit*);
+ void Reset(Unit* owner) { Initialize(owner); }
+ bool Update(Unit*, const uint32&);
MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; }
private:
@@ -72,7 +72,7 @@ class AssistanceDistractMovementGenerator : public DistractMovementGenerator
DistractMovementGenerator(timer) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
- void Finalize(Unit& unit);
+ void Finalize(Unit*);
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
index 07a5761517e..c8a7c8f8f8d 100755
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp
@@ -27,35 +27,35 @@
//----- Point Movement Generator
template<class T>
-void PointMovementGenerator<T>::Initialize(T &unit)
+void PointMovementGenerator<T>::Initialize(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
- init.MoveTo(i_x, i_y, i_z);
+ init.MoveTo(i_x, i_y, i_z, m_generatePath);
if (speed > 0.0f)
init.SetVelocity(speed);
init.Launch();
}
template<class T>
-bool PointMovementGenerator<T>::Update(T &unit, const uint32 & /*diff*/)
+bool PointMovementGenerator<T>::Update(T* unit, const uint32& /*diff*/)
{
- if (!&unit)
+ if (!unit)
return false;
- if (unit.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (unit->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
{
- unit.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- unit.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING_MOVE);
- if (i_recalculateSpeed && !unit.movespline->Finalized())
+ if (i_recalculateSpeed && !unit->movespline->Finalized())
{
i_recalculateSpeed = false;
Movement::MoveSplineInit init(unit);
@@ -65,73 +65,74 @@ bool PointMovementGenerator<T>::Update(T &unit, const uint32 & /*diff*/)
init.Launch();
}
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
template<class T>
-void PointMovementGenerator<T>::Finalize(T &unit)
+void PointMovementGenerator<T>::Finalize(T* unit)
{
- unit.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ if (unit->HasUnitState(UNIT_STATE_CHARGING))
+ unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
- if (unit.movespline->Finalized())
+ if (unit->movespline->Finalized())
MovementInform(unit);
}
template<class T>
-void PointMovementGenerator<T>::Reset(T &unit)
+void PointMovementGenerator<T>::Reset(T* unit)
{
- if (!unit.IsStopped())
- unit.StopMoving();
+ if (!unit->IsStopped())
+ unit->StopMoving();
- unit.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
template<class T>
-void PointMovementGenerator<T>::MovementInform(T & /*unit*/)
+void PointMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
-template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature* unit)
{
- if (unit.AI())
- unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+ if (unit->AI())
+ unit->AI()->MovementInform(POINT_MOTION_TYPE, id);
}
-template void PointMovementGenerator<Player>::Initialize(Player&);
-template void PointMovementGenerator<Creature>::Initialize(Creature&);
-template void PointMovementGenerator<Player>::Finalize(Player&);
-template void PointMovementGenerator<Creature>::Finalize(Creature&);
-template void PointMovementGenerator<Player>::Reset(Player&);
-template void PointMovementGenerator<Creature>::Reset(Creature&);
-template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &);
-template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &);
+template void PointMovementGenerator<Player>::Initialize(Player*);
+template void PointMovementGenerator<Creature>::Initialize(Creature*);
+template void PointMovementGenerator<Player>::Finalize(Player*);
+template void PointMovementGenerator<Creature>::Finalize(Creature*);
+template void PointMovementGenerator<Player>::Reset(Player*);
+template void PointMovementGenerator<Creature>::Reset(Creature*);
+template bool PointMovementGenerator<Player>::Update(Player*, const uint32&);
+template bool PointMovementGenerator<Creature>::Update(Creature*, const uint32&);
-void AssistanceMovementGenerator::Finalize(Unit &unit)
+void AssistanceMovementGenerator::Finalize(Unit* unit)
{
- unit.ToCreature()->SetNoCallAssistance(false);
- unit.ToCreature()->CallAssistance();
- if (unit.isAlive())
- unit.GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
+ unit->ToCreature()->SetNoCallAssistance(false);
+ unit->ToCreature()->CallAssistance();
+ if (unit->isAlive())
+ unit->GetMotionMaster()->MoveSeekAssistanceDistract(sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY));
}
-bool EffectMovementGenerator::Update(Unit &unit, const uint32&)
+bool EffectMovementGenerator::Update(Unit* unit, const uint32&)
{
- return !unit.movespline->Finalized();
+ return !unit->movespline->Finalized();
}
-void EffectMovementGenerator::Finalize(Unit &unit)
+void EffectMovementGenerator::Finalize(Unit* unit)
{
- if (unit.GetTypeId() != TYPEID_UNIT)
+ if (unit->GetTypeId() != TYPEID_UNIT)
return;
- if (((Creature&)unit).AI())
- ((Creature&)unit).AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
+ if (unit->ToCreature()->AI())
+ unit->ToCreature()->AI()->MovementInform(EFFECT_MOTION_TYPE, m_Id);
// Need restore previous movement since we have no proper states system
- //if (unit.isAlive() && !unit.HasUnitState(UNIT_STATE_CONFUSED|UNIT_STATE_FLEEING))
- //{
- // if (Unit * victim = unit.getVictim())
- // unit.GetMotionMaster()->MoveChase(victim);
- // else
- // unit.GetMotionMaster()->Initialize();
- //}
+ if (unit->isAlive() && !unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING))
+ {
+ if (Unit* victim = unit->getVictim())
+ unit->GetMotionMaster()->MoveChase(victim);
+ else
+ unit->GetMotionMaster()->Initialize();
+ }
}
diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
index d2833a5ee10..4111690627e 100755..100644
--- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.h
@@ -26,25 +26,26 @@ template<class T>
class PointMovementGenerator : public MovementGeneratorMedium< T, PointMovementGenerator<T> >
{
public:
- PointMovementGenerator(uint32 _id, float _x, float _y, float _z, float _speed = 0.0f) : id(_id),
- i_x(_x), i_y(_y), i_z(_z), speed(_speed) {}
+ PointMovementGenerator(uint32 _id, float _x, float _y, float _z, bool _generatePath, float _speed = 0.0f) : id(_id),
+ i_x(_x), i_y(_y), i_z(_z), m_generatePath(_generatePath), speed(_speed) {}
- void Initialize(T &);
- void Finalize(T &);
- void Reset(T &);
- bool Update(T &, const uint32 &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, const uint32 &);
- void MovementInform(T &);
+ void MovementInform(T*);
void unitSpeedChanged() { i_recalculateSpeed = true; }
MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; }
- bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; }
+ void GetDestination(float& x, float& y, float& z) const { x = i_x; y = i_y; z = i_z; }
private:
uint32 id;
float i_x, i_y, i_z;
float speed;
+ bool m_generatePath;
bool i_recalculateSpeed;
};
@@ -52,10 +53,10 @@ class AssistanceMovementGenerator : public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
- PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+ PointMovementGenerator<Creature>(0, _x, _y, _z, true) {}
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
- void Finalize(Unit &);
+ void Finalize(Unit*);
};
// Does almost nothing - just doesn't allows previous movegen interrupt current effect.
@@ -63,10 +64,10 @@ class EffectMovementGenerator : public MovementGenerator
{
public:
explicit EffectMovementGenerator(uint32 Id) : m_Id(Id) {}
- void Initialize(Unit &) {}
- void Finalize(Unit &unit);
- void Reset(Unit &) {}
- bool Update(Unit &u, const uint32&);
+ void Initialize(Unit*) {}
+ void Finalize(Unit*);
+ void Reset(Unit*) {}
+ bool Update(Unit*, const uint32&);
MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; }
private:
uint32 m_Id;
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
index 84cd9e88295..3e55b9679c6 100755
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp
@@ -33,16 +33,16 @@
#endif
template<>
-void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
+void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature)
{
float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ;
- creature.GetHomePosition(respX, respY, respZ, respO);
- Map const* map = creature.GetBaseMap();
+ creature->GetHomePosition(respX, respY, respZ, respO);
+ Map const* map = creature->GetBaseMap();
// For 2D/3D system selection
//bool is_land_ok = creature.CanWalk(); // not used?
//bool is_water_ok = creature.CanSwim(); // not used?
- bool is_air_ok = creature.CanFly();
+ bool is_air_ok = creature->CanFly();
const float angle = float(rand_norm()) * static_cast<float>(M_PI*2.0f);
const float range = float(rand_norm()) * wander_distance;
@@ -77,17 +77,17 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
// The fastest way to get an accurate result 90% of the time.
// Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false);
if (fabs(destZ - respZ) > travelDistZ) // Map check
{
// Vmap Horizontal or above
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ - 2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ - 2.0f, true);
if (fabs(destZ - respZ) > travelDistZ)
{
// Vmap Higher
- destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
+ destZ = map->GetHeight(creature->GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);
// let's forget this bad coords where a z cannot be find and retry at next tick
if (fabs(destZ - respZ) > travelDistZ)
@@ -101,7 +101,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
else
i_nextMoveTime.Reset(urand(500, 10000));
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(destX, destY, destZ);
@@ -109,47 +109,47 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(destX, destY, destZ);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(destX, destY, destZ);
}
template<>
-void RandomMovementGenerator<Creature>::Initialize(Creature &creature)
+void RandomMovementGenerator<Creature>::Initialize(Creature* creature)
{
- if (!creature.isAlive())
+ if (!creature->isAlive())
return;
if (!wander_distance)
- wander_distance = creature.GetRespawnRadius();
+ wander_distance = creature->GetRespawnRadius();
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
_setRandomLocation(creature);
}
template<>
-void RandomMovementGenerator<Creature>::Reset(Creature &creature)
+void RandomMovementGenerator<Creature>::Reset(Creature* creature)
{
Initialize(creature);
}
template<>
-void RandomMovementGenerator<Creature>::Finalize(Creature &creature)
+void RandomMovementGenerator<Creature>::Finalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
template<>
-bool RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 diff)
+bool RandomMovementGenerator<Creature>::Update(Creature* creature, const uint32 diff)
{
- if (creature.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
+ if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED))
{
i_nextMoveTime.Reset(0); // Expire the timer
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
- if (creature.movespline->Finalized())
+ if (creature->movespline->Finalized())
{
i_nextMoveTime.Update(diff);
if (i_nextMoveTime.Passed())
@@ -159,14 +159,14 @@ bool RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32
}
template<>
-bool RandomMovementGenerator<Creature>::GetResetPosition(Creature &creature, float& x, float& y, float& z)
+bool RandomMovementGenerator<Creature>::GetResetPosition(Creature* creature, float& x, float& y, float& z)
{
float radius;
- creature.GetRespawnPosition(x, y, z, NULL, &radius);
+ creature->GetRespawnPosition(x, y, z, NULL, &radius);
// use current if in range
- if (creature.IsWithinDist2d(x,y,radius))
- creature.GetPosition(x,y,z);
+ if (creature->IsWithinDist2d(x,y,radius))
+ creature->GetPosition(x,y,z);
return true;
}
diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
index 07ec3647052..ca370de6ac3 100755
--- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h
@@ -27,12 +27,12 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen
public:
RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) {}
- void _setRandomLocation(T &);
- void Initialize(T &);
- void Finalize(T &);
- void Reset(T &);
- bool Update(T &, const uint32);
- bool GetResetPosition(T&, float& x, float& y, float& z);
+ void _setRandomLocation(T*);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ bool Update(T*, const uint32);
+ bool GetResetPosition(T*, float& x, float& y, float& z);
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
private:
TimeTrackerSmall i_nextMoveTime;
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index fdff5a92564..9784c19b4fd 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -26,15 +26,13 @@
#include "MoveSpline.h"
#include "Player.h"
-#include <cmath>
-
template<class T, typename D>
-void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
+void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return;
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
return;
float x, y, z;
@@ -51,44 +49,37 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
// else
if (!i_offset)
{
- if (i_target->IsWithinMeleeRange(&owner))
+ if (i_target->IsWithinMeleeRange(owner))
return;
// to nearest random contact position
- i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f);
+ i_target->GetRandomContactPoint(owner, x, y, z, 0, MELEE_RANGE - 0.5f);
}
else
{
- if (i_target->IsWithinDistInMap(&owner, i_offset + 1.0f))
+ if (i_target->IsWithinDistInMap(owner, i_offset + 1.0f))
return;
// to at i_offset distance from target and i_angle from target facing
- i_target->GetClosePoint(x, y, z, owner.GetObjectSize(), i_offset, i_angle);
+ i_target->GetClosePoint(x, y, z, owner->GetObjectSize(), i_offset, i_angle);
}
- /*
- We MUST not check the distance difference and avoid setting the new location for smaller distances.
- By that we risk having far too many GetContactPoint() calls freezing the whole system.
- In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
- some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
- If the distance to the target it too large to ignore,
- but the distance to the new contact point is short enough to be ignored,
- we will calculate a new contact point each update loop, but will never move to it.
- The system will freeze.
- ralf
-
- //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
- float bothObjectSize = i_target->GetObjectBoundingRadius() + owner.GetObjectBoundingRadius() + CONTACT_DISTANCE;
- if ( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
- return;
- */
+ if (!i_path)
+ i_path = new PathGenerator(owner);
+ // allow pets following their master to cheat while generating paths
+ bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->isPet()
+ && owner->HasUnitState(UNIT_STATE_FOLLOW));
+ i_path->CalculatePath(x, y, z, forceDest);
+ if (i_path->getPathType() & PATHFIND_NOPATH)
+ return;
D::_addUnitStateMove(owner);
i_targetReached = false;
i_recalculateTravel = false;
+ owner->AddUnitState(UNIT_STATE_CHASE);
Movement::MoveSplineInit init(owner);
- init.MoveTo(x,y,z);
+ init.MovebyPath(i_path->getPath());
init.SetWalk(((D*)this)->EnableWalking());
init.Launch();
}
@@ -120,25 +111,25 @@ void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature>
}
template<class T, typename D>
-bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_diff)
+bool TargetedMovementGeneratorMedium<T,D>::Update(T* owner, const uint32& time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return false;
- if (!owner.isAlive())
+ if (!owner || !owner->isAlive())
return true;
- if (owner.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (owner->HasUnitState(UNIT_STATE_NOT_MOVE))
{
D::_clearUnitStateMove(owner);
return true;
}
-
+
// prevent movement while casting spells with cast time or channel time
- if (owner.HasUnitState(UNIT_STATE_CASTING))
+ if (owner->HasUnitState(UNIT_STATE_CASTING))
{
- if (!owner.IsStopped())
- owner.StopMoving();
+ if (!owner->IsStopped())
+ owner->StopMoving();
return true;
}
@@ -152,19 +143,26 @@ bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_
i_recheckDistance.Update(time_diff);
if (i_recheckDistance.Passed())
{
- i_recheckDistance.Reset(50);
+ i_recheckDistance.Reset(100);
//More distance let have better performance, less distance let have more sensitive reaction at target move.
- float allowed_dist = i_target->GetObjectSize() + owner.GetObjectSize() + MELEE_RANGE - 0.5f;
- float dist = (owner.movespline->FinalDestination() - G3D::Vector3(i_target->GetPositionX(),i_target->GetPositionY(),i_target->GetPositionZ())).squaredLength();
- if (dist >= allowed_dist * allowed_dist)
+ float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+ G3D::Vector3 dest = owner->movespline->FinalDestination();
+
+ bool targetMoved = false;
+ if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly())
+ targetMoved = !i_target->IsWithinDist3d(dest.x, dest.y, dest.z, allowed_dist);
+ else
+ targetMoved = !i_target->IsWithinDist2d(dest.x, dest.y, allowed_dist);
+
+ if (targetMoved)
_setTargetLocation(owner);
}
- if (owner.movespline->Finalized())
+ if (owner->movespline->Finalized())
{
static_cast<D*>(this)->MovementInform(owner);
- if (i_angle == 0.f && !owner.HasInArc(0.01f, i_target.getTarget()))
- owner.SetInFront(i_target.getTarget());
+ if (i_angle == 0.f && !owner->HasInArc(0.01f, i_target.getTarget()))
+ owner->SetInFront(i_target.getTarget());
if (!i_targetReached)
{
@@ -182,50 +180,50 @@ bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_
//-----------------------------------------------//
template<class T>
-void ChaseMovementGenerator<T>::_reachTarget(T &owner)
+void ChaseMovementGenerator<T>::_reachTarget(T* owner)
{
- if (owner.IsWithinMeleeRange(this->i_target.getTarget()))
- owner.Attack(this->i_target.getTarget(),true);
+ if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
+ owner->Attack(this->i_target.getTarget(),true);
}
template<>
-void ChaseMovementGenerator<Player>::Initialize(Player &owner)
+void ChaseMovementGenerator<Player>::Initialize(Player* owner)
{
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
_setTargetLocation(owner);
}
template<>
-void ChaseMovementGenerator<Creature>::Initialize(Creature &owner)
+void ChaseMovementGenerator<Creature>::Initialize(Creature* owner)
{
- owner.SetWalk(false);
- owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner->SetWalk(false);
+ owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
_setTargetLocation(owner);
}
template<class T>
-void ChaseMovementGenerator<T>::Finalize(T &owner)
+void ChaseMovementGenerator<T>::Finalize(T* owner)
{
- owner.ClearUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE);
+ owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
}
template<class T>
-void ChaseMovementGenerator<T>::Reset(T &owner)
+void ChaseMovementGenerator<T>::Reset(T* owner)
{
Initialize(owner);
}
template<class T>
-void ChaseMovementGenerator<T>::MovementInform(T & /*unit*/)
+void ChaseMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
-void ChaseMovementGenerator<Creature>::MovementInform(Creature &unit)
+void ChaseMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+ if (unit->AI())
+ unit->AI()->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
@@ -242,85 +240,85 @@ bool FollowMovementGenerator<Player>::EnableWalking() const
}
template<>
-void FollowMovementGenerator<Player>::_updateSpeed(Player &/*u*/)
+void FollowMovementGenerator<Player>::_updateSpeed(Player* /*u*/)
{
// nothing to do for Player
}
template<>
-void FollowMovementGenerator<Creature>::_updateSpeed(Creature &u)
+void FollowMovementGenerator<Creature>::_updateSpeed(Creature* u)
{
// pet only sync speed with owner
- if (!((Creature&)u).isPet() || !i_target.isValid() || i_target->GetGUID() != u.GetOwnerGUID())
+ if (!u->isPet() || !i_target.isValid() || i_target->GetGUID() != u->GetOwnerGUID())
return;
- u.UpdateSpeed(MOVE_RUN,true);
- u.UpdateSpeed(MOVE_WALK,true);
- u.UpdateSpeed(MOVE_SWIM,true);
+ u->UpdateSpeed(MOVE_RUN,true);
+ u->UpdateSpeed(MOVE_WALK,true);
+ u->UpdateSpeed(MOVE_SWIM,true);
}
template<>
-void FollowMovementGenerator<Player>::Initialize(Player &owner)
+void FollowMovementGenerator<Player>::Initialize(Player* owner)
{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
_setTargetLocation(owner);
}
template<>
-void FollowMovementGenerator<Creature>::Initialize(Creature &owner)
+void FollowMovementGenerator<Creature>::Initialize(Creature* owner)
{
- owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
_setTargetLocation(owner);
}
template<class T>
-void FollowMovementGenerator<T>::Finalize(T &owner)
+void FollowMovementGenerator<T>::Finalize(T* owner)
{
- owner.ClearUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE);
+ owner->ClearUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE);
_updateSpeed(owner);
}
template<class T>
-void FollowMovementGenerator<T>::Reset(T &owner)
+void FollowMovementGenerator<T>::Reset(T* owner)
{
Initialize(owner);
}
template<class T>
-void FollowMovementGenerator<T>::MovementInform(T & /*unit*/)
+void FollowMovementGenerator<T>::MovementInform(T* /*unit*/)
{
}
template<>
-void FollowMovementGenerator<Creature>::MovementInform(Creature &unit)
+void FollowMovementGenerator<Creature>::MovementInform(Creature* unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
- if (unit.AI())
- unit.AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
+ if (unit->AI())
+ unit->AI()->MovementInform(FOLLOW_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
//-----------------------------------------------//
-template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player &);
-template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature &);
-template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::Update(Player &, const uint32 &);
-template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::Update(Player &, const uint32 &);
-template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::Update(Creature &, const uint32 &);
-template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::Update(Creature &, const uint32 &);
-
-template void ChaseMovementGenerator<Player>::_reachTarget(Player &);
-template void ChaseMovementGenerator<Creature>::_reachTarget(Creature &);
-template void ChaseMovementGenerator<Player>::Finalize(Player &);
-template void ChaseMovementGenerator<Creature>::Finalize(Creature &);
-template void ChaseMovementGenerator<Player>::Reset(Player &);
-template void ChaseMovementGenerator<Creature>::Reset(Creature &);
-template void ChaseMovementGenerator<Player>::MovementInform(Player &unit);
-
-template void FollowMovementGenerator<Player>::Finalize(Player &);
-template void FollowMovementGenerator<Creature>::Finalize(Creature &);
-template void FollowMovementGenerator<Player>::Reset(Player &);
-template void FollowMovementGenerator<Creature>::Reset(Creature &);
-template void FollowMovementGenerator<Player>::MovementInform(Player &unit);
+template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player*);
+template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player*);
+template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*);
+template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*);
+template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::Update(Player*, const uint32&);
+template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::Update(Player*, const uint32&);
+template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::Update(Creature*, const uint32&);
+template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::Update(Creature*, const uint32&);
+
+template void ChaseMovementGenerator<Player>::_reachTarget(Player*);
+template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*);
+template void ChaseMovementGenerator<Player>::Finalize(Player*);
+template void ChaseMovementGenerator<Creature>::Finalize(Creature*);
+template void ChaseMovementGenerator<Player>::Reset(Player*);
+template void ChaseMovementGenerator<Creature>::Reset(Creature*);
+template void ChaseMovementGenerator<Player>::MovementInform(Player*);
+
+template void FollowMovementGenerator<Player>::Finalize(Player*);
+template void FollowMovementGenerator<Creature>::Finalize(Creature*);
+template void FollowMovementGenerator<Player>::Reset(Player*);
+template void FollowMovementGenerator<Creature>::Reset(Creature*);
+template void FollowMovementGenerator<Player>::MovementInform(Player*);
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
index 29fd73624e1..0ca688fb0df 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.h
@@ -23,11 +23,12 @@
#include "FollowerReference.h"
#include "Timer.h"
#include "Unit.h"
+#include "PathGenerator.h"
class TargetedMovementGeneratorBase
{
public:
- TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); }
+ TargetedMovementGeneratorBase(Unit* target) { i_target.link(target, this); }
void stopFollowing() { }
protected:
FollowerReference i_target;
@@ -37,79 +38,80 @@ template<class T, typename D>
class TargetedMovementGeneratorMedium : public MovementGeneratorMedium< T, D >, public TargetedMovementGeneratorBase
{
protected:
- TargetedMovementGeneratorMedium(Unit &target, float offset, float angle) :
- TargetedMovementGeneratorBase(target), i_recheckDistance(0),
+ TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) :
+ TargetedMovementGeneratorBase(target), i_recheckDistance(0), i_path(NULL),
i_offset(offset), i_angle(angle),
i_recalculateTravel(false), i_targetReached(false)
{
}
- ~TargetedMovementGeneratorMedium() {}
+ ~TargetedMovementGeneratorMedium() { delete i_path; }
public:
- bool Update(T &, const uint32 &);
+ bool Update(T*, const uint32 &);
Unit* GetTarget() const { return i_target.getTarget(); }
- void unitSpeedChanged() { i_recalculateTravel=true; }
+ void unitSpeedChanged() { i_recalculateTravel = true; }
void UpdateFinalDistance(float fDistance);
-
+ bool IsReachable() const { return (i_path) ? (i_path->getPathType() & PATHFIND_NORMAL) : true; }
protected:
- void _setTargetLocation(T &);
+ void _setTargetLocation(T*);
TimeTrackerSmall i_recheckDistance;
float i_offset;
float i_angle;
bool i_recalculateTravel : 1;
bool i_targetReached : 1;
+ PathGenerator* i_path;
};
template<class T>
class ChaseMovementGenerator : public TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >
{
public:
- ChaseMovementGenerator(Unit &target)
+ ChaseMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target) {}
- ChaseMovementGenerator(Unit &target, float offset, float angle)
+ ChaseMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, ChaseMovementGenerator<T> >(target, offset, angle) {}
~ChaseMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
- void Initialize(T &);
- void Finalize(T &);
- void Reset(T &);
- void MovementInform(T &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_CHASE_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_CHASE_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_CHASE_MOVE); }
bool EnableWalking() const { return false;}
- bool _lostTarget(T &u) const { return u.getVictim() != this->GetTarget(); }
- void _reachTarget(T &);
+ bool _lostTarget(T* u) const { return u->getVictim() != this->GetTarget(); }
+ void _reachTarget(T*);
};
template<class T>
class FollowMovementGenerator : public TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >
{
public:
- FollowMovementGenerator(Unit &target)
+ FollowMovementGenerator(Unit* target)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target){}
- FollowMovementGenerator(Unit &target, float offset, float angle)
+ FollowMovementGenerator(Unit* target, float offset, float angle)
: TargetedMovementGeneratorMedium<T, FollowMovementGenerator<T> >(target, offset, angle) {}
~FollowMovementGenerator() {}
MovementGeneratorType GetMovementGeneratorType() { return FOLLOW_MOTION_TYPE; }
- void Initialize(T &);
- void Finalize(T &);
- void Reset(T &);
- void MovementInform(T &);
+ void Initialize(T*);
+ void Finalize(T*);
+ void Reset(T*);
+ void MovementInform(T*);
- static void _clearUnitStateMove(T &u) { u.ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
- static void _addUnitStateMove(T &u) { u.AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _clearUnitStateMove(T* u) { u->ClearUnitState(UNIT_STATE_FOLLOW_MOVE); }
+ static void _addUnitStateMove(T* u) { u->AddUnitState(UNIT_STATE_FOLLOW_MOVE); }
bool EnableWalking() const;
- bool _lostTarget(T &) const { return false; }
- void _reachTarget(T &) {}
+ bool _lostTarget(T*) const { return false; }
+ void _reachTarget(T*) {}
private:
- void _updateSpeed(T &u);
+ void _updateSpeed(T* u);
};
#endif
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index 12b85b10922..c576d7389f3 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -31,64 +31,64 @@
#include "MoveSplineInit.h"
#include "MoveSpline.h"
-void WaypointMovementGenerator<Creature>::LoadPath(Creature &creature)
+void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature)
{
if (!path_id)
- path_id = creature.GetWaypointPath();
+ path_id = creature->GetWaypointPath();
i_path = sWaypointMgr->GetPath(path_id);
if (!i_path)
{
// No movement found for entry
- sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature.GetName(), creature.GetEntry(), creature.GetGUIDLow(), path_id);
+ sLog->outError(LOG_FILTER_SQL, "WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path id: %u", creature->GetName(), creature->GetEntry(), creature->GetGUIDLow(), path_id);
return;
}
StartMoveNow(creature);
}
-void WaypointMovementGenerator<Creature>::Initialize(Creature &creature)
+void WaypointMovementGenerator<Creature>::Initialize(Creature* creature)
{
LoadPath(creature);
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
}
-void WaypointMovementGenerator<Creature>::Finalize(Creature &creature)
+void WaypointMovementGenerator<Creature>::Finalize(Creature* creature)
{
- creature.ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
- creature.SetWalk(false);
+ creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->SetWalk(false);
}
-void WaypointMovementGenerator<Creature>::Reset(Creature &creature)
+void WaypointMovementGenerator<Creature>::Reset(Creature* creature)
{
- creature.AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE);
StartMoveNow(creature);
}
-void WaypointMovementGenerator<Creature>::OnArrived(Creature& creature)
+void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature)
{
if (!i_path || i_path->empty())
return;
if (m_isArrivalDone)
return;
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
m_isArrivalDone = true;
if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance)
{
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature.GetGUID());
- creature.GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, &creature, NULL);
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Creature movement start script %u at point %u for "UI64FMTD".", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID());
+ creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, NULL);
}
// Inform script
MovementInform(creature);
- creature.UpdateWaypointID(i_currentNode);
+ creature->UpdateWaypointID(i_currentNode);
Stop(i_path->at(i_currentNode)->delay);
}
-bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
+bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature)
{
if (!i_path || i_path->empty())
return false;
@@ -99,8 +99,8 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
{
if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint
{
- creature.SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature.GetOrientation());
- creature.GetMotionMaster()->Initialize();
+ creature->SetHomePosition(i_path->at(i_currentNode)->x, i_path->at(i_currentNode)->y, i_path->at(i_currentNode)->z, creature->GetOrientation());
+ creature->GetMotionMaster()->Initialize();
return false;
}
@@ -111,7 +111,7 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
m_isArrivalDone = false;
- creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->AddUnitState(UNIT_STATE_ROAMING_MOVE);
Movement::MoveSplineInit init(creature);
init.MoveTo(node->x, node->y, node->z);
@@ -124,19 +124,19 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature &creature)
init.Launch();
//Call for creature group update
- if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
- creature.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
+ if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature)
+ creature->GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
return true;
}
-bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+bool WaypointMovementGenerator<Creature>::Update(Creature* creature, const uint32& diff)
{
// Waypoint movement can be switched on/off
// This is quite handy for escort quests and other stuff
- if (creature.HasUnitState(UNIT_STATE_NOT_MOVE))
+ if (creature->HasUnitState(UNIT_STATE_NOT_MOVE))
{
- creature.ClearUnitState(UNIT_STATE_ROAMING_MOVE);
+ creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE);
return true;
}
// prevent a crash at empty waypoint path.
@@ -150,9 +150,9 @@ bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint3
}
else
{
- if (creature.IsStopped())
+ if (creature->IsStopped())
Stop(STOP_TIME_FOR_PLAYER);
- else if (creature.movespline->Finalized())
+ else if (creature->movespline->Finalized())
{
OnArrived(creature);
return StartMove(creature);
@@ -161,13 +161,13 @@ bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint3
return true;
}
-void WaypointMovementGenerator<Creature>::MovementInform(Creature &creature)
+void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature)
{
- if (creature.AI())
- creature.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
+ if (creature->AI())
+ creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
}
-bool WaypointMovementGenerator<Creature>::GetResetPosition(Creature&, float& x, float& y, float& z)
+bool WaypointMovementGenerator<Creature>::GetResetPosition(Creature*, float& x, float& y, float& z)
{
// prevent a crash at empty waypoint path.
if (!i_path || i_path->empty())
@@ -196,37 +196,37 @@ uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const
return i_path->size();
}
-void FlightPathMovementGenerator::Initialize(Player &player)
+void FlightPathMovementGenerator::Initialize(Player* player)
{
Reset(player);
InitEndGridInfo();
}
-void FlightPathMovementGenerator::Finalize(Player& player)
+void FlightPathMovementGenerator::Finalize(Player* player)
{
// remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack)
- player.ClearUnitState(UNIT_STATE_IN_FLIGHT);
+ player->ClearUnitState(UNIT_STATE_IN_FLIGHT);
- player.Dismount();
- player.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->Dismount();
+ player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
- if (player.m_taxi.empty())
+ if (player->m_taxi.empty())
{
- player.getHostileRefManager().setOnlineOfflineState(true);
+ player->getHostileRefManager().setOnlineOfflineState(true);
// update z position to ground and orientation for landing point
// this prevent cheating with landing point at lags
// when client side flight end early in comparison server side
- player.StopMoving();
+ player->StopMoving();
}
}
#define PLAYER_FLIGHT_SPEED 32.0f
-void FlightPathMovementGenerator::Reset(Player & player)
+void FlightPathMovementGenerator::Reset(Player* player)
{
- player.getHostileRefManager().setOnlineOfflineState(false);
- player.AddUnitState(UNIT_STATE_IN_FLIGHT);
- player.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
+ player->getHostileRefManager().setOnlineOfflineState(false);
+ player->AddUnitState(UNIT_STATE_IN_FLIGHT);
+ player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
Movement::MoveSplineInit init(player);
uint32 end = GetPathAtMapEnd();
@@ -241,9 +241,9 @@ void FlightPathMovementGenerator::Reset(Player & player)
init.Launch();
}
-bool FlightPathMovementGenerator::Update(Player &player, const uint32& /*diff*/)
+bool FlightPathMovementGenerator::Update(Player* player, const uint32& /*diff*/)
{
- uint32 pointId = (uint32)player.movespline->currentPathIdx();
+ uint32 pointId = (uint32)player->movespline->currentPathIdx();
if (pointId > i_currentNode)
{
bool departureEvent = true;
@@ -279,16 +279,16 @@ void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
}
}
-void FlightPathMovementGenerator::DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure)
+void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure)
{
if (uint32 eventid = departure ? node.departureEventID : node.arrivalEventID)
{
- sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player.GetName());
- player.GetMap()->ScriptsStart(sEventScripts, eventid, &player, &player);
+ sLog->outDebug(LOG_FILTER_MAPSCRIPTS, "Taxi %s event %u of node %u of path %u for player %s", departure ? "departure" : "arrival", eventid, node.index, node.path, player->GetName());
+ player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player);
}
}
-bool FlightPathMovementGenerator::GetResetPosition(Player&, float& x, float& y, float& z)
+bool FlightPathMovementGenerator::GetResetPosition(Player*, float& x, float& y, float& z)
{
const TaxiPathNodeEntry& node = (*i_path)[i_currentNode];
x = node.x; y = node.y; z = node.z;
@@ -320,331 +320,3 @@ void FlightPathMovementGenerator::PreloadEndGrid()
else
sLog->outInfo(LOG_FILTER_GENERAL, "Unable to determine map to preload flightmaster grid");
}
-
-
-//
-// Unique1's ASTAR Pathfinding Code... For future use & reference...
-//
-
-#ifdef __PATHFINDING__
-
-int GetFCost(int to, int num, int parentNum, float *gcost); // Below...
-
-int ShortenASTARRoute(short int *pathlist, int number)
-{ // Wrote this to make the routes a little smarter (shorter)... No point looping back to the same places... Unique1
- short int temppathlist[MAX_PATHLIST_NODES];
- int count = 0;
- // int count2 = 0;
- int temp, temp2;
- int link;
- int upto = 0;
-
- for (temp = number; temp >= 0; temp--)
- {
- qboolean shortened = qfalse;
-
- for (temp2 = 0; temp2 < temp; temp2++)
- {
- for (link = 0; link < nodes[pathlist[temp]].enodenum; link++)
- {
- if (nodes[pathlist[temp]].links[link].flags & PATH_BLOCKED)
- continue;
-
- //if ((bot->client->ps.eFlags & EF_TANK) && nodes[bot->current_node].links[link].flags & PATH_NOTANKS) //if this path is blocked, skip it
- // continue;
-
- //if (nodes[nodes[pathlist[temp]].links[link].targetNode].origin[2] > nodes[pathlist[temp]].origin[2] + 32)
- // continue;
-
- if (nodes[pathlist[temp]].links[link].targetNode == pathlist[temp2])
- { // Found a shorter route...
- //if (OrgVisible(nodes[pathlist[temp2]].origin, nodes[pathlist[temp]].origin, -1))
- {
- temppathlist[count] = pathlist[temp2];
- temp = temp2;
- ++count;
- shortened = qtrue;
- }
- }
- }
- }
-
- if (!shortened)
- {
- temppathlist[count] = pathlist[temp];
- ++count;
- }
- }
-
- upto = count;
-
- for (temp = 0; temp < count; temp++)
- {
- pathlist[temp] = temppathlist[upto];
- --upto;
- }
-
- G_Printf("ShortenASTARRoute: Path size reduced from %i to %i nodes...n", number, count);
- return count;
-}
-
-/*
-===========================================================================
-CreatePathAStar
-This function uses the A* pathfinding algorithm to determine the
-shortest path between any two nodes.
-It's fairly complex, so I'm not really going to explain it much.
-Look up A* and binary heaps for more info.
-pathlist stores the ideal path between the nodes, in reverse order,
-and the return value is the number of nodes in that path
-===========================================================================
-*/
-int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
-{
- //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES
- //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it
- short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index
- float gcost[MAX_NODES];
- int fcost[MAX_NODES];
- char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type
- short int parent[MAX_NODES];
-
- short int numOpen = 0;
- short int atNode, temp, newnode=-1;
- qboolean found = qfalse;
- int count = -1;
- float gc;
- int i, u, v, m;
- vec3_t vec;
-
- //clear out all the arrays
- memset(openlist, 0, sizeof(short int)*(MAX_NODES+1));
- memset(fcost, 0, sizeof(int)*MAX_NODES);
- memset(list, 0, sizeof(char)*MAX_NODES);
- memset(parent, 0, sizeof(short int)*MAX_NODES);
- memset(gcost, -1, sizeof(float)*MAX_NODES);
-
- //make sure we have valid data before calculating everything
- if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to))
- return -1;
-
- openlist[1] = from; //add the starting node to the open list
- ++numOpen;
- gcost[from] = 0; //its f and g costs are obviously 0
- fcost[from] = 0;
-
- while (1)
- {
- if (numOpen != 0) //if there are still items in the open list
- {
- //pop the top item off of the list
- atNode = openlist[1];
- list[atNode] = 2; //put the node on the closed list so we don't check it again
- --numOpen;
-
- openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position
- v = 1;
-
- //this while loop reorders the list so that the new lowest fcost is at the top again
- while (1)
- {
- u = v;
- if ((2*u+1) < numOpen) //if both children exist
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- if (fcost[openlist[v]] >= fcost[openlist[2*u+1]])
- v = 2*u+1;
- }
- else
- {
- if ((2*u) < numOpen) //if only one child exists
- {
- if (fcost[openlist[u]] >= fcost[openlist[2*u]])
- v = 2*u;
- }
- }
-
- if (u != v) //if they're out of order, swap this item with its parent
- {
- temp = openlist[u];
- openlist[u] = openlist[v];
- openlist[v] = temp;
- }
- else
- break;
- }
-
- for (i = 0; i < nodes[atNode].enodenum; ++i) //loop through all the links for this node
- {
- newnode = nodes[atNode].links[i].targetNode;
-
- //if this path is blocked, skip it
- if (nodes[atNode].links[i].flags & PATH_BLOCKED)
- continue;
- //if this path is blocked, skip it
- if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS)
- continue;
- //skip any unreachable nodes
- if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES))
- continue;
- if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS))
- continue;
-
- if (list[newnode] == 2) //if this node is on the closed list, skip it
- continue;
-
- if (list[newnode] != 1) //if this node is not already on the open list
- {
- openlist[++numOpen] = newnode; //add the new node to the open list
- list[newnode] = 1;
- parent[newnode] = atNode; //record the node's parent
-
- if (newnode == to) //if we've found the goal, don't keep computing paths!
- break; //this will break the 'for' and go all the way to 'if (list[to] == 1)'
-
- //store it's f cost value
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //this loop re-orders the heap so that the lowest fcost is at the top
- m = numOpen;
- while (m != 1) //while this item isn't at the top of the heap already
- {
- //if it has a lower fcost than its parent
- if (fcost[openlist[m]] <= fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- }
- else //if this node is already on the open list
- {
- gc = gcost[atNode];
- VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec);
- gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path
-
- if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before)
- {
- parent[newnode] = atNode; //set the new parent for this node
- gcost[newnode] = gc; //and the new g cost
-
- for (i = 1; i < numOpen; ++i) //loop through all the items on the open list
- {
- if (openlist[i] == newnode) //find this node in the list
- {
- //calculate the new fcost and store it
- fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost);
-
- //reorder the list again, with the lowest fcost item on top
- m = i;
- while (m != 1)
- {
- //if the item has a lower fcost than it's parent
- if (fcost[openlist[m]] < fcost[openlist[m/2]])
- {
- temp = openlist[m/2];
- openlist[m/2] = openlist[m];
- openlist[m] = temp; //swap them
- m /= 2;
- }
- else
- break;
- }
- break; //exit the 'for' loop because we already changed this node
- } //if
- } //for
- } //if (gc < gcost[newnode])
- } //if (list[newnode] != 1) --> else
- } //for (loop through links)
- } //if (numOpen != 0)
- else
- {
- found = qfalse; //there is no path between these nodes
- break;
- }
-
- if (list[to] == 1) //if the destination node is on the open list, we're done
- {
- found = qtrue;
- break;
- }
- } //while (1)
-
- if (found == qtrue) //if we found a path
- {
- //G_Printf("%s - path found!n", bot->client->pers.netname);
- count = 0;
-
- temp = to; //start at the end point
- while (temp != from) //travel along the path (backwards) until we reach the starting point
- {
- pathlist[count++] = temp; //add the node to the pathlist and increment the count
- temp = parent[temp]; //move to the parent of this node to continue the path
- }
-
- pathlist[count++] = from; //add the beginning node to the end of the pathlist
-
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- }
- else
- {
- //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to);
- count = CreateDumbRoute(from, to, pathlist);
-
- if (count > 0)
- {
- #ifdef __BOT_SHORTEN_ROUTING__
- count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1
- #endif //__BOT_SHORTEN_ROUTING__
- return count;
- }
- }
-
- return count; //return the number of nodes in the path, -1 if not found
-}
-
-/*
-===========================================================================
-GetFCost
-Utility function used by A* pathfinding to calculate the
-cost to move between nodes towards a goal. Using the A*
-algorithm F = G + H, G here is the distance along the node
-paths the bot must travel, and H is the straight-line distance
-to the goal node.
-Returned as an int because more precision is unnecessary and it
-will slightly speed up heap access
-===========================================================================
-*/
-int GetFCost(int to, int num, int parentNum, float *gcost)
-{
- float gc = 0;
- float hc = 0;
- vec3_t v;
-
- if (gcost[num] == -1)
- {
- if (parentNum != -1)
- {
- gc = gcost[parentNum];
- VectorSubtract(nodes[num].origin, nodes[parentNum].origin, v);
- gc += VectorLength(v);
- }
- gcost[num] = gc;
- }
- else
- gc = gcost[num];
-
- VectorSubtract(nodes[to].origin, nodes[num].origin, v);
- hc = VectorLength(v);
-
- return (int)(gc + hc);
-}
-#endif //__PATHFINDING__
-
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
index 0d8f047c83a..5230fe3b0e3 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h
@@ -65,19 +65,19 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true)
: i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) {}
~WaypointMovementGenerator() { i_path = NULL; }
- void Initialize(Creature &);
- void Finalize(Creature &);
- void Reset(Creature &);
- bool Update(Creature &, const uint32 &diff);
+ void Initialize(Creature*);
+ void Finalize(Creature*);
+ void Reset(Creature*);
+ bool Update(Creature*, const uint32 &diff);
- void MovementInform(Creature &);
+ void MovementInform(Creature*);
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
// now path movement implmementation
- void LoadPath(Creature &c);
+ void LoadPath(Creature*);
- bool GetResetPosition(Creature&, float& x, float& y, float& z);
+ bool GetResetPosition(Creature*, float& x, float& y, float& z);
private:
@@ -91,10 +91,10 @@ class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Crea
return i_nextMoveTime.Passed();
}
- void OnArrived(Creature&);
- bool StartMove(Creature&);
+ void OnArrived(Creature*);
+ bool StartMove(Creature*);
- void StartMoveNow(Creature& creature)
+ void StartMoveNow(Creature* creature)
{
i_nextMoveTime.Reset(0);
StartMove(creature);
@@ -118,10 +118,10 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
i_path = &pathnodes;
i_currentNode = startNode;
}
- void Initialize(Player &);
- void Reset(Player &);
- void Finalize(Player &);
- bool Update(Player &, const uint32&);
+ void Initialize(Player*);
+ void Reset(Player*);
+ void Finalize(Player*);
+ bool Update(Player*, const uint32&);
MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; }
TaxiPathNodeList const& GetPath() { return *i_path; }
@@ -129,9 +129,9 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig
bool HasArrived() const { return (i_currentNode >= i_path->size()); }
void SetCurrentNodeAfterTeleport();
void SkipCurrentNode() { ++i_currentNode; }
- void DoEventIfAny(Player& player, TaxiPathNodeEntry const& node, bool departure);
+ void DoEventIfAny(Player* player, TaxiPathNodeEntry const& node, bool departure);
- bool GetResetPosition(Player&, float& x, float& y, float& z);
+ bool GetResetPosition(Player*, float& x, float& y, float& z);
void InitEndGridInfo();
void PreloadEndGrid();
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
new file mode 100644
index 00000000000..4d7707020e1
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -0,0 +1,808 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathGenerator.h"
+#include "Map.h"
+#include "Creature.h"
+#include "MMapFactory.h"
+#include "MMapManager.h"
+#include "Log.h"
+
+#include "DetourCommon.h"
+#include "DetourNavMeshQuery.h"
+
+////////////////// PathGenerator //////////////////
+PathGenerator::PathGenerator(const Unit* owner) :
+ m_polyLength(0), m_type(PATHFIND_BLANK),
+ m_useStraightPath(false), m_forceDestination(false), m_pointPathLimit(MAX_POINT_PATH_LENGTH),
+ m_sourceUnit(owner), m_navMesh(NULL), m_navMeshQuery(NULL)
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::PathGenerator for %u \n", m_sourceUnit->GetGUIDLow());
+
+ uint32 mapId = m_sourceUnit->GetMapId();
+ if (MMAP::MMapFactory::IsPathfindingEnabled(mapId))
+ {
+ MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager();
+ m_navMesh = mmap->GetNavMesh(mapId);
+ m_navMeshQuery = mmap->GetNavMeshQuery(mapId, m_sourceUnit->GetInstanceId());
+ }
+
+ createFilter();
+}
+
+PathGenerator::~PathGenerator()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::~PathGenerator() for %u \n", m_sourceUnit->GetGUIDLow());
+}
+
+bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest)
+{
+ if (!Trinity::IsValidMapCoord(destX, destY, destZ) ||
+ !Trinity::IsValidMapCoord(m_sourceUnit->GetPositionX(), m_sourceUnit->GetPositionY(), m_sourceUnit->GetPositionZ()))
+ return false;
+
+ Vector3 oldDest = getEndPosition();
+ Vector3 dest(destX, destY, destZ);
+ setEndPosition(dest);
+
+ float x, y, z;
+ m_sourceUnit->GetPosition(x, y, z);
+ Vector3 start(x, y, z);
+ setStartPosition(start);
+
+ m_forceDestination = forceDest;
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::CalculatePath() for %u \n", m_sourceUnit->GetGUIDLow());
+
+ // make sure navMesh works - we can run on map w/o mmap
+ // check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
+ if (!m_navMesh || !m_navMeshQuery || m_sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) ||
+ !HaveTile(start) || !HaveTile(dest))
+ {
+ BuildShortcut();
+ m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return true;
+ }
+
+ updateFilter();
+
+ // check if destination moved - if not we can optimize something here
+ // we are following old, precalculated path?
+ float dist = m_sourceUnit->GetObjectSize();
+ if (inRange(oldDest, dest, dist, dist) && m_pathPoints.size() > 2)
+ {
+ // our target is not moving - we just coming closer
+ // we are moving on precalculated path - enjoy the ride
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::CalculatePath:: precalculated path\n");
+
+ m_pathPoints.erase(m_pathPoints.begin());
+ return false;
+ }
+ else
+ {
+ // target moved, so we need to update the poly path
+ BuildPolyPath(start, dest);
+ return true;
+ }
+}
+
+dtPolyRef PathGenerator::getPathPolyByPosition(const dtPolyRef *polyPath, uint32 polyPathSize, const float* point, float *distance) const
+{
+ if (!polyPath || !polyPathSize)
+ return INVALID_POLYREF;
+
+ dtPolyRef nearestPoly = INVALID_POLYREF;
+ float minDist2d = FLT_MAX;
+ float minDist3d = 0.0f;
+
+ for (uint32 i = 0; i < polyPathSize; ++i)
+ {
+ float closestPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(polyPath[i], point, closestPoint))
+ continue;
+
+ float d = dtVdist2DSqr(point, closestPoint);
+ if (d < minDist2d)
+ {
+ minDist2d = d;
+ nearestPoly = polyPath[i];
+ minDist3d = dtVdistSqr(point, closestPoint);
+ }
+
+ if(minDist2d < 1.0f) // shortcut out - close enough for us
+ break;
+ }
+
+ if (distance)
+ *distance = dtSqrt(minDist3d);
+
+ return (minDist2d < 3.0f) ? nearestPoly : INVALID_POLYREF;
+}
+
+dtPolyRef PathGenerator::getPolyByLocation(const float* point, float *distance) const
+{
+ // first we check the current path
+ // if the current path doesn't contain the current poly,
+ // we need to use the expensive navMesh.findNearestPoly
+ dtPolyRef polyRef = getPathPolyByPosition(m_pathPolyRefs, m_polyLength, point, distance);
+ if (polyRef != INVALID_POLYREF)
+ return polyRef;
+
+ // we don't have it in our old path
+ // try to get it by findNearestPoly()
+ // first try with low search box
+ float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; // bounds of poly search area
+ float closestPoint[VERTEX_SIZE] = {0.0f, 0.0f, 0.0f};
+ dtStatus result = m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ // still nothing ..
+ // try with bigger search box
+ extents[1] = 200.0f;
+ result = m_navMeshQuery->findNearestPoly(point, extents, &m_filter, &polyRef, closestPoint);
+ if (DT_SUCCESS == result && polyRef != INVALID_POLYREF)
+ {
+ *distance = dtVdist(closestPoint, point);
+ return polyRef;
+ }
+
+ return INVALID_POLYREF;
+}
+
+void PathGenerator::BuildPolyPath(const Vector3 &startPos, const Vector3 &endPos)
+{
+ // *** getting start/end poly logic ***
+
+ float distToStartPoly, distToEndPoly;
+ float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x};
+ float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x};
+
+ dtPolyRef startPoly = getPolyByLocation(startPoint, &distToStartPoly);
+ dtPolyRef endPoly = getPolyByLocation(endPoint, &distToEndPoly);
+
+ // we have a hole in our mesh
+ // make shortcut path and mark it as NOPATH ( with flying and swimming exception )
+ // its up to caller how he will use this info
+ if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n");
+ BuildShortcut();
+ bool path = m_sourceUnit->GetTypeId() == TYPEID_UNIT && m_sourceUnit->ToCreature()->CanFly();
+
+ bool waterPath = m_sourceUnit->GetTypeId() == TYPEID_UNIT && m_sourceUnit->ToCreature()->canSwim();
+ if (waterPath)
+ {
+ // Check both start and end points, if they're both in water, then we can *safely* let the creature move
+ for (int i = 0; i < m_pathPoints.size(); ++i)
+ {
+ LiquidData data;
+ m_sourceUnit->GetBaseMap()->getLiquidStatus(m_pathPoints[i].x, m_pathPoints[i].y, m_pathPoints[i].z, MAP_ALL_LIQUIDS, &data);
+ // One of the points is not in the water, cancel movement.
+ if (data.type_flags == MAP_LIQUID_TYPE_NO_WATER)
+ {
+ waterPath = false;
+ break;
+ }
+ }
+ }
+
+ m_type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH;
+ return;
+ }
+
+ // we may need a better number here
+ bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f);
+ if (farFromPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly);
+
+ bool buildShotrcut = false;
+ if (m_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* owner = (Creature*)m_sourceUnit;
+
+ Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos;
+ if (m_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z))
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: underWater case\n");
+ if (owner->canSwim())
+ buildShotrcut = true;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: flying case\n");
+ if (owner->CanFly())
+ buildShotrcut = true;
+ }
+ }
+
+ if (buildShotrcut)
+ {
+ BuildShortcut();
+ m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ return;
+ }
+ else
+ {
+ float closestPoint[VERTEX_SIZE];
+ // we may want to use closestPointOnPolyBoundary instead
+ if (DT_SUCCESS == m_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint))
+ {
+ dtVcopy(endPoint, closestPoint);
+ setActualEndPosition(Vector3(endPoint[2],endPoint[0],endPoint[1]));
+ }
+
+ m_type = PATHFIND_INCOMPLETE;
+ }
+ }
+
+ // *** poly path generating logic ***
+
+ // start and end are on same polygon
+ // just need to move in straight line
+ if (startPoly == endPoly)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPoly == endPoly)\n");
+
+ BuildShortcut();
+
+ m_pathPolyRefs[0] = startPoly;
+ m_polyLength = 1;
+
+ m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL;
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: path type %d\n", m_type);
+ return;
+ }
+
+ // look for startPoly/endPoly in current path
+ // TODO: we can merge it with getPathPolyByPosition() loop
+ bool startPolyFound = false;
+ bool endPolyFound = false;
+ uint32 pathStartIndex, pathEndIndex;
+
+ if (m_polyLength)
+ {
+ for (pathStartIndex = 0; pathStartIndex < m_polyLength; ++pathStartIndex)
+ {
+ // here to carch few bugs
+ ASSERT(m_pathPolyRefs[pathStartIndex] != INVALID_POLYREF);
+
+ if (m_pathPolyRefs[pathStartIndex] == startPoly)
+ {
+ startPolyFound = true;
+ break;
+ }
+ }
+
+ for (pathEndIndex = m_polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex)
+ if (m_pathPolyRefs[pathEndIndex] == endPoly)
+ {
+ endPolyFound = true;
+ break;
+ }
+ }
+
+ if (startPolyFound && endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n");
+
+ // we moved along the path and the target did not move out of our old poly-path
+ // our path is a simple subpath case, we have all the data we need
+ // just "cut" it out
+
+ m_polyLength = pathEndIndex - pathStartIndex + 1;
+ memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, m_polyLength*sizeof(dtPolyRef));
+ }
+ else if (startPolyFound && !endPolyFound)
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n");
+
+ // we are moving on the old path but target moved out
+ // so we have atleast part of poly-path ready
+
+ m_polyLength -= pathStartIndex;
+
+ // try to adjust the suffix of the path instead of recalculating entire length
+ // at given interval the target cannot get too far from its last location
+ // thus we have less poly to cover
+ // sub-path of optimal path is optimal
+
+ // take ~80% of the original length
+ // TODO : play with the values here
+ uint32 prefixPolyLength = uint32(m_polyLength*0.8f + 0.5f);
+ memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, prefixPolyLength*sizeof(dtPolyRef));
+
+ dtPolyRef suffixStartPoly = m_pathPolyRefs[prefixPolyLength-1];
+
+ // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
+ float suffixEndPoint[VERTEX_SIZE];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
+ // try to recover by using prev polyref
+ --prefixPolyLength;
+ suffixStartPoly = m_pathPolyRefs[prefixPolyLength-1];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint))
+ {
+ // suffixStartPoly is still invalid, error state
+ BuildShortcut();
+ m_type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // generate suffix
+ uint32 suffixPolyLength = 0;
+ dtStatus dtResult = m_navMeshQuery->findPath(
+ suffixStartPoly, // start polygon
+ endPoly, // end polygon
+ suffixEndPoint, // start position
+ endPoint, // end position
+ &m_filter, // polygon search filter
+ m_pathPolyRefs + prefixPolyLength - 1, // [out] path
+ (int*)&suffixPolyLength,
+ MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path
+
+ if (!suffixPolyLength || dtResult != DT_SUCCESS)
+ {
+ // this is probably an error state, but we'll leave it
+ // and hopefully recover on the next Update
+ // we still need to copy our preffix
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", m_sourceUnit->GetGUIDLow());
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n",m_polyLength, prefixPolyLength, suffixPolyLength);
+
+ // new path = prefix + suffix - overlap
+ m_polyLength = prefixPolyLength + suffixPolyLength - 1;
+ }
+ else
+ {
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n");
+
+ // either we have no path at all -> first run
+ // or something went really wrong -> we aren't moving along the path to the target
+ // just generate new path
+
+ // free and invalidate old path data
+ clear();
+
+ dtStatus dtResult = m_navMeshQuery->findPath(
+ startPoly, // start polygon
+ endPoly, // end polygon
+ startPoint, // start position
+ endPoint, // end position
+ &m_filter, // polygon search filter
+ m_pathPolyRefs, // [out] path
+ (int*)&m_polyLength,
+ MAX_PATH_LENGTH); // max number of polygons in output path
+
+ if (!m_polyLength || dtResult != DT_SUCCESS)
+ {
+ // only happens if we passed bad data to findPath(), or navmesh is messed up
+ sLog->outError(LOG_FILTER_MAPS, "%u's Path Build failed: 0 length path", m_sourceUnit->GetGUIDLow());
+ BuildShortcut();
+ m_type = PATHFIND_NOPATH;
+ return;
+ }
+ }
+
+ // by now we know what type of path we can get
+ if (m_pathPolyRefs[m_polyLength - 1] == endPoly && !(m_type & PATHFIND_INCOMPLETE))
+ m_type = PATHFIND_NORMAL;
+ else
+ m_type = PATHFIND_INCOMPLETE;
+
+ // generate the point-path out of our up-to-date poly-path
+ BuildPointPath(startPoint, endPoint);
+}
+
+void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoint)
+{
+ float pathPoints[MAX_POINT_PATH_LENGTH*VERTEX_SIZE];
+ uint32 pointCount = 0;
+ dtStatus dtResult = DT_FAILURE;
+ if (m_useStraightPath)
+ {
+ dtResult = m_navMeshQuery->findStraightPath(
+ startPoint, // start position
+ endPoint, // end position
+ m_pathPolyRefs, // current path
+ m_polyLength, // lenth of current path
+ pathPoints, // [out] path corner points
+ NULL, // [out] flags
+ NULL, // [out] shortened path
+ (int*)&pointCount,
+ m_pointPathLimit); // maximum number of points/polygons to use
+ }
+ else
+ {
+ dtResult = findSmoothPath(
+ startPoint, // start position
+ endPoint, // end position
+ m_pathPolyRefs, // current path
+ m_polyLength, // length of current path
+ pathPoints, // [out] path corner points
+ (int*)&pointCount,
+ m_pointPathLimit); // maximum number of points
+ }
+
+ if (pointCount < 2 || dtResult != DT_SUCCESS)
+ {
+ // only happens if pass bad data to findStraightPath or navmesh is broken
+ // single point paths can be generated here
+ // TODO : check the exact cases
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned\n", pointCount);
+ BuildShortcut();
+ m_type = PATHFIND_NOPATH;
+ return;
+ }
+
+ m_pathPoints.resize(pointCount);
+ for (uint32 i = 0; i < pointCount; ++i)
+ m_pathPoints[i] = Vector3(pathPoints[i*VERTEX_SIZE+2], pathPoints[i*VERTEX_SIZE], pathPoints[i*VERTEX_SIZE+1]);
+
+ NormalizePath();
+
+ // first point is always our current location - we need the next one
+ setActualEndPosition(m_pathPoints[pointCount-1]);
+
+ // force the given destination, if needed
+ if(m_forceDestination &&
+ (!(m_type & PATHFIND_NORMAL) || !inRange(getEndPosition(), getActualEndPosition(), 1.0f, 1.0f)))
+ {
+ // we may want to keep partial subpath
+ if(dist3DSqr(getActualEndPosition(), getEndPosition()) <
+ 0.3f * dist3DSqr(getStartPosition(), getEndPosition()))
+ {
+ setActualEndPosition(getEndPosition());
+ m_pathPoints[m_pathPoints.size()-1] = getEndPosition();
+ }
+ else
+ {
+ setActualEndPosition(getEndPosition());
+ BuildShortcut();
+ }
+
+ m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH);
+ }
+
+ sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", m_type, pointCount, m_polyLength);
+}
+
+void PathGenerator::NormalizePath()
+{
+ for (uint32 i = 0; i < m_pathPoints.size(); ++i)
+ m_sourceUnit->UpdateAllowedPositionZ(m_pathPoints[i].x, m_pathPoints[i].y, m_pathPoints[i].z);
+}
+
+void PathGenerator::BuildShortcut()
+{
+ sLog->outDebug(LOG_FILTER_MAPS, "++ BuildShortcut :: making shortcut\n");
+
+ clear();
+
+ // make two point path, our curr pos is the start, and dest is the end
+ m_pathPoints.resize(2);
+
+ // set start and a default next position
+ m_pathPoints[0] = getStartPosition();
+ m_pathPoints[1] = getActualEndPosition();
+
+ NormalizePath();
+
+ m_type = PATHFIND_SHORTCUT;
+}
+
+void PathGenerator::createFilter()
+{
+ uint16 includeFlags = 0;
+ uint16 excludeFlags = 0;
+
+ if (m_sourceUnit->GetTypeId() == TYPEID_UNIT)
+ {
+ Creature* creature = (Creature*)m_sourceUnit;
+ if (creature->canWalk())
+ includeFlags |= NAV_GROUND; // walk
+
+ // creatures don't take environmental damage
+ if (creature->canSwim())
+ includeFlags |= (NAV_WATER | NAV_MAGMA | NAV_SLIME); // swim
+ }
+ else if (m_sourceUnit->GetTypeId() == TYPEID_PLAYER)
+ {
+ // perfect support not possible, just stay 'safe'
+ includeFlags |= (NAV_GROUND | NAV_WATER);
+ }
+
+ m_filter.setIncludeFlags(includeFlags);
+ m_filter.setExcludeFlags(excludeFlags);
+
+ updateFilter();
+}
+
+void PathGenerator::updateFilter()
+{
+ // allow creatures to cheat and use different movement types if they are moved
+ // forcefully into terrain they can't normally move in
+ if (m_sourceUnit->IsInWater() || m_sourceUnit->IsUnderWater())
+ {
+ uint16 includedFlags = m_filter.getIncludeFlags();
+ includedFlags |= getNavTerrain(m_sourceUnit->GetPositionX(),
+ m_sourceUnit->GetPositionY(),
+ m_sourceUnit->GetPositionZ());
+
+ m_filter.setIncludeFlags(includedFlags);
+ }
+}
+
+NavTerrain PathGenerator::getNavTerrain(float x, float y, float z)
+{
+ LiquidData data;
+ m_sourceUnit->GetBaseMap()->getLiquidStatus(x, y, z, MAP_ALL_LIQUIDS, &data);
+
+ switch (data.type_flags)
+ {
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ return NAV_WATER;
+ case MAP_LIQUID_TYPE_MAGMA:
+ return NAV_MAGMA;
+ case MAP_LIQUID_TYPE_SLIME:
+ return NAV_SLIME;
+ default:
+ return NAV_GROUND;
+ }
+}
+
+bool PathGenerator::HaveTile(const Vector3 &p) const
+{
+ int tx, ty;
+ float point[VERTEX_SIZE] = {p.y, p.z, p.x};
+
+ m_navMesh->calcTileLoc(point, &tx, &ty);
+ return (m_navMesh->getTileAt(tx, ty) != NULL);
+}
+
+uint32 PathGenerator::fixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath,
+ const dtPolyRef* visited, uint32 nvisited)
+{
+ int32 furthestPath = -1;
+ int32 furthestVisited = -1;
+
+ // Find furthest common polygon.
+ for (int32 i = npath-1; i >= 0; --i)
+ {
+ bool found = false;
+ for (int32 j = nvisited-1; j >= 0; --j)
+ {
+ if (path[i] == visited[j])
+ {
+ furthestPath = i;
+ furthestVisited = j;
+ found = true;
+ }
+ }
+ if (found)
+ break;
+ }
+
+ // If no intersection found just return current path.
+ if (furthestPath == -1 || furthestVisited == -1)
+ return npath;
+
+ // Concatenate paths.
+
+ // Adjust beginning of the buffer to include the visited.
+ uint32 req = nvisited - furthestVisited;
+ uint32 orig = uint32(furthestPath+1) < npath ? furthestPath+1 : npath;
+ uint32 size = npath-orig > 0 ? npath-orig : 0;
+ if (req+size > maxPath)
+ size = maxPath-req;
+
+ if (size)
+ memmove(path+req, path+orig, size*sizeof(dtPolyRef));
+
+ // Store visited
+ for (uint32 i = 0; i < req; ++i)
+ path[i] = visited[(nvisited-1)-i];
+
+ return req+size;
+}
+
+bool PathGenerator::getSteerTarget(const float* startPos, const float* endPos,
+ float minTargetDist, const dtPolyRef* path, uint32 pathSize,
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef)
+{
+ // Find steer target.
+ static const uint32 MAX_STEER_POINTS = 3;
+ float steerPath[MAX_STEER_POINTS*VERTEX_SIZE];
+ unsigned char steerPathFlags[MAX_STEER_POINTS];
+ dtPolyRef steerPathPolys[MAX_STEER_POINTS];
+ uint32 nsteerPath = 0;
+ dtStatus dtResult = m_navMeshQuery->findStraightPath(startPos, endPos, path, pathSize,
+ steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS);
+ if (!nsteerPath || DT_SUCCESS != dtResult)
+ return false;
+
+ // Find vertex far enough to steer to.
+ uint32 ns = 0;
+ while (ns < nsteerPath)
+ {
+ // Stop at Off-Mesh link or when point is further than slop away.
+ if ((steerPathFlags[ns] & DT_STRAIGHTPATH_OFFMESH_CONNECTION) ||
+ !inRangeYZX(&steerPath[ns*VERTEX_SIZE], startPos, minTargetDist, 1000.0f))
+ break;
+ ns++;
+ }
+ // Failed to find good point to steer to.
+ if (ns >= nsteerPath)
+ return false;
+
+ dtVcopy(steerPos, &steerPath[ns*VERTEX_SIZE]);
+ steerPos[1] = startPos[1]; // keep Z value
+ steerPosFlag = steerPathFlags[ns];
+ steerPosRef = steerPathPolys[ns];
+
+ return true;
+}
+
+dtStatus PathGenerator::findSmoothPath(const float* startPos, const float* endPos,
+ const dtPolyRef* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 maxSmoothPathSize)
+{
+ *smoothPathSize = 0;
+ uint32 nsmoothPath = 0;
+
+ dtPolyRef polys[MAX_PATH_LENGTH];
+ memcpy(polys, polyPath, sizeof(dtPolyRef)*polyPathSize);
+ uint32 npolys = polyPathSize;
+
+ float iterPos[VERTEX_SIZE], targetPos[VERTEX_SIZE];
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos))
+ return DT_FAILURE;
+
+ if (DT_SUCCESS != m_navMeshQuery->closestPointOnPolyBoundary(polys[npolys-1], endPos, targetPos))
+ return DT_FAILURE;
+
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+
+ // Move towards target a small advancement at a time until target reached or
+ // when ran out of memory to store the path.
+ while (npolys && nsmoothPath < maxSmoothPathSize)
+ {
+ // Find location to steer towards.
+ float steerPos[VERTEX_SIZE];
+ unsigned char steerPosFlag;
+ dtPolyRef steerPosRef = INVALID_POLYREF;
+
+ if (!getSteerTarget(iterPos, targetPos, SMOOTH_PATH_SLOP, polys, npolys, steerPos, steerPosFlag, steerPosRef))
+ break;
+
+ bool endOfPath = (steerPosFlag & DT_STRAIGHTPATH_END);
+ bool offMeshConnection = (steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION);
+
+ // Find movement delta.
+ float delta[VERTEX_SIZE];
+ dtVsub(delta, steerPos, iterPos);
+ float len = dtSqrt(dtVdot(delta,delta));
+ // If the steer target is end of path or off-mesh link, do not move past the location.
+ if ((endOfPath || offMeshConnection) && len < SMOOTH_PATH_STEP_SIZE)
+ len = 1.0f;
+ else
+ len = SMOOTH_PATH_STEP_SIZE / len;
+
+ float moveTgt[VERTEX_SIZE];
+ dtVmad(moveTgt, iterPos, delta, len);
+
+ // Move
+ float result[VERTEX_SIZE];
+ const static uint32 MAX_VISIT_POLY = 16;
+ dtPolyRef visited[MAX_VISIT_POLY];
+
+ uint32 nvisited = 0;
+ m_navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &m_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY);
+ npolys = fixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited);
+
+ m_navMeshQuery->getPolyHeight(polys[0], result, &result[1]);
+ result[1] += 0.5f;
+ dtVcopy(iterPos, result);
+
+ // Handle end of path and off-mesh links when close enough.
+ if (endOfPath && inRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Reached end of path.
+ dtVcopy(iterPos, targetPos);
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ break;
+ }
+ else if (offMeshConnection && inRangeYZX(iterPos, steerPos, SMOOTH_PATH_SLOP, 1.0f))
+ {
+ // Advance the path up to and over the off-mesh connection.
+ dtPolyRef prevRef = INVALID_POLYREF;
+ dtPolyRef polyRef = polys[0];
+ uint32 npos = 0;
+ while (npos < npolys && polyRef != steerPosRef)
+ {
+ prevRef = polyRef;
+ polyRef = polys[npos];
+ npos++;
+ }
+
+ for (uint32 i = npos; i < npolys; ++i)
+ polys[i-npos] = polys[i];
+
+ npolys -= npos;
+
+ // Handle the connection.
+ float startPos[VERTEX_SIZE], endPos[VERTEX_SIZE];
+ if (DT_SUCCESS == m_navMesh->getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos))
+ {
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], startPos);
+ nsmoothPath++;
+ }
+ // Move position at the other side of the off-mesh link.
+ dtVcopy(iterPos, endPos);
+ m_navMeshQuery->getPolyHeight(polys[0], iterPos, &iterPos[1]);
+ iterPos[1] += 0.5f;
+ }
+ }
+
+ // Store results.
+ if (nsmoothPath < maxSmoothPathSize)
+ {
+ dtVcopy(&smoothPath[nsmoothPath*VERTEX_SIZE], iterPos);
+ nsmoothPath++;
+ }
+ }
+
+ *smoothPathSize = nsmoothPath;
+
+ // this is most likely a loop
+ return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE;
+}
+
+bool PathGenerator::inRangeYZX(const float* v1, const float* v2, float r, float h) const
+{
+ const float dx = v2[0] - v1[0];
+ const float dy = v2[1] - v1[1]; // elevation
+ const float dz = v2[2] - v1[2];
+ return (dx*dx + dz*dz) < r*r && fabsf(dy) < h;
+}
+
+bool PathGenerator::inRange(const Vector3 &p1, const Vector3 &p2, float r, float h) const
+{
+ Vector3 d = p1-p2;
+ return (d.x*d.x + d.y*d.y) < r*r && fabsf(d.z) < h;
+}
+
+float PathGenerator::dist3DSqr(const Vector3 &p1, const Vector3 &p2) const
+{
+ return (p1-p2).squaredLength();
+}
diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h
new file mode 100644
index 00000000000..a0d925cb11f
--- /dev/null
+++ b/src/server/game/Movement/PathGenerator.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PATH_INFO_H
+#define _PATH_INFO_H
+
+#include "SharedDefines.h"
+#include "DetourNavMesh.h"
+#include "DetourNavMeshQuery.h"
+#include "MoveSplineInitArgs.h"
+
+using Movement::Vector3;
+using Movement::PointsArray;
+
+class Unit;
+
+// 74*4.0f=296y number_of_points*interval = max_path_len
+// this is way more than actual evade range
+// I think we can safely cut those down even more
+#define MAX_PATH_LENGTH 74
+#define MAX_POINT_PATH_LENGTH 74
+
+#define SMOOTH_PATH_STEP_SIZE 4.0f
+#define SMOOTH_PATH_SLOP 0.3f
+
+#define VERTEX_SIZE 3
+#define INVALID_POLYREF 0
+
+enum PathType
+{
+ PATHFIND_BLANK = 0x0000, // path not built yet
+ PATHFIND_NORMAL = 0x0001, // normal path
+ PATHFIND_SHORTCUT = 0x0002, // travel through obstacles, terrain, air, etc (old behavior)
+ PATHFIND_INCOMPLETE = 0x0004, // we have partial path to follow - getting closer to target
+ PATHFIND_NOPATH = 0x0008, // no valid path at all or error in generating one
+ PATHFIND_NOT_USING_PATH = 0x0010 // used when we are either flying/swiming or on map w/o mmaps
+};
+
+class PathGenerator
+{
+ public:
+ PathGenerator(Unit const* owner);
+ ~PathGenerator();
+
+ // Calculate the path from owner to given destination
+ // return: true if new path was calculated, false otherwise (no change needed)
+ bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false);
+
+ // option setters - use optional
+ void SetUseStraightPath(bool useStraightPath) { m_useStraightPath = useStraightPath; };
+ void setPathLengthLimit(float distance) { m_pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); };
+
+ // result getters
+ Vector3 getStartPosition() const { return m_startPosition; }
+ Vector3 getEndPosition() const { return m_endPosition; }
+ Vector3 getActualEndPosition() const { return m_actualEndPosition; }
+
+ PointsArray& getPath() { return m_pathPoints; }
+ PathType getPathType() const { return m_type; }
+
+ private:
+
+ dtPolyRef m_pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references
+ uint32 m_polyLength; // number of polygons in the path
+
+ PointsArray m_pathPoints; // our actual (x,y,z) path to the target
+ PathType m_type; // tells what kind of path this is
+
+ bool m_useStraightPath; // type of path will be generated
+ bool m_forceDestination; // when set, we will always arrive at given point
+ uint32 m_pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH)
+
+ Vector3 m_startPosition; // {x, y, z} of current location
+ Vector3 m_endPosition; // {x, y, z} of the destination
+ Vector3 m_actualEndPosition;// {x, y, z} of the closest possible point to given destination
+
+ const Unit* const m_sourceUnit; // the unit that is moving
+ const dtNavMesh* m_navMesh; // the nav mesh
+ const dtNavMeshQuery* m_navMeshQuery; // the nav mesh query used to find the path
+
+ dtQueryFilter m_filter; // use single filter for all movements, update it when needed
+
+ void setStartPosition(Vector3 point) { m_startPosition = point; }
+ void setEndPosition(Vector3 point) { m_actualEndPosition = point; m_endPosition = point; }
+ void setActualEndPosition(Vector3 point) { m_actualEndPosition = point; }
+
+ void NormalizePath();
+
+ void clear()
+ {
+ m_polyLength = 0;
+ m_pathPoints.clear();
+ }
+
+ bool inRange(const Vector3 &p1, const Vector3 &p2, float r, float h) const;
+ float dist3DSqr(const Vector3 &p1, const Vector3 &p2) const;
+ bool inRangeYZX(const float* v1, const float* v2, float r, float h) const;
+
+ dtPolyRef getPathPolyByPosition(const dtPolyRef *polyPath, uint32 polyPathSize, const float* point, float *distance = NULL) const;
+ dtPolyRef getPolyByLocation(const float* point, float *distance) const;
+ bool HaveTile(const Vector3 &p) const;
+
+ void BuildPolyPath(const Vector3 &startPos, const Vector3 &endPos);
+ void BuildPointPath(const float *startPoint, const float *endPoint);
+ void BuildShortcut();
+
+ NavTerrain getNavTerrain(float x, float y, float z);
+ void createFilter();
+ void updateFilter();
+
+ // smooth path aux functions
+ uint32 fixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath,
+ const dtPolyRef* visited, uint32 nvisited);
+ bool getSteerTarget(const float* startPos, const float* endPos, float minTargetDist,
+ const dtPolyRef* path, uint32 pathSize, float* steerPos,
+ unsigned char& steerPosFlag, dtPolyRef& steerPosRef);
+ dtStatus findSmoothPath(const float* startPos, const float* endPos,
+ const dtPolyRef* polyPath, uint32 polyPathSize,
+ float* smoothPath, int* smoothPathSize, uint32 smoothPathMaxSize);
+};
+
+#endif
diff --git a/src/server/game/Movement/Spline/MoveSpline.h b/src/server/game/Movement/Spline/MoveSpline.h
index d4b19b21634..4ec8d2c78ac 100644
--- a/src/server/game/Movement/Spline/MoveSpline.h
+++ b/src/server/game/Movement/Spline/MoveSpline.h
@@ -77,7 +77,6 @@ namespace Movement
UpdateResult _updateState(int32& ms_time_diff);
int32 next_timestamp() const { return spline.length(point_Idx+1);}
int32 segment_time_elapsed() const { return next_timestamp()-time_passed;}
- int32 Duration() const { return spline.length();}
int32 timeElapsed() const { return Duration() - time_passed;}
int32 timePassed() const { return time_passed;}
@@ -88,7 +87,7 @@ namespace Movement
void _Interrupt() { splineflags.done = true;}
public:
-
+ int32 Duration() const { return spline.length();}
void Initialize(const MoveSplineInitArgs&);
bool Initialized() const { return !spline.empty();}
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp
index c539dd3cc39..8167ff36de2 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.cpp
+++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp
@@ -56,36 +56,36 @@ namespace Movement
return MOVE_RUN;
}
- void MoveSplineInit::Launch()
+ int32 MoveSplineInit::Launch()
{
- MoveSpline& move_spline = *unit.movespline;
+ MoveSpline& move_spline = *unit->movespline;
bool transport = false;
- Location real_position(unit.GetPositionX(), unit.GetPositionY(), unit.GetPositionZMinusOffset(), unit.GetOrientation());
+ Location real_position(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZMinusOffset(), unit->GetOrientation());
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- if (unit.HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit.GetTransGUID())
+ if (unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID())
{
transport = true;
- real_position.x = unit.GetTransOffsetX();
- real_position.y = unit.GetTransOffsetY();
- real_position.z = unit.GetTransOffsetZ();
- real_position.orientation = unit.GetTransOffsetO();
+ real_position.x = unit->GetTransOffsetX();
+ real_position.y = unit->GetTransOffsetY();
+ real_position.z = unit->GetTransOffsetZ();
+ real_position.orientation = unit->GetTransOffsetO();
}
// there is a big chance that current position is unknown if current state is not finalized, need compute it
- // this also allows calculate spline position and update map position in much greater intervals
+ // this also allows CalculatePath spline position and update map position in much greater intervals
if (!move_spline.Finalized())
real_position = move_spline.ComputePosition();
// should i do the things that user should do? - no.
if (args.path.empty())
- return;
+ return 0;
// corrent first vertex
args.path[0] = real_position;
args.initialOrientation = real_position.orientation;
- uint32 moveFlags = unit.m_movementInfo.GetMovementFlags();
+ uint32 moveFlags = unit->m_movementInfo.GetMovementFlags();
if (args.flags.walkmode)
moveFlags |= MOVEMENTFLAG_WALKING;
else
@@ -94,39 +94,41 @@ namespace Movement
moveFlags |= (MOVEMENTFLAG_SPLINE_ENABLED|MOVEMENTFLAG_FORWARD);
if (!args.HasVelocity)
- args.velocity = unit.GetSpeed(SelectSpeedType(moveFlags));
+ args.velocity = unit->GetSpeed(SelectSpeedType(moveFlags));
if (!args.Validate())
- return;
+ return 0;
if (moveFlags & MOVEMENTFLAG_ROOT)
moveFlags &= ~MOVEMENTFLAG_MASK_MOVING;
- unit.m_movementInfo.SetMovementFlags((MovementFlags)moveFlags);
+ unit->m_movementInfo.SetMovementFlags((MovementFlags)moveFlags);
move_spline.Initialize(args);
WorldPacket data(!transport ? SMSG_MONSTER_MOVE : SMSG_MONSTER_MOVE_TRANSPORT, 64);
- data.append(unit.GetPackGUID());
+ data.append(unit->GetPackGUID());
if (transport)
{
- data.appendPackGUID(unit.GetTransGUID());
- data << int8(unit.GetTransSeat());
+ data.appendPackGUID(unit->GetTransGUID());
+ data << int8(unit->GetTransSeat());
}
PacketBuilder::WriteMonsterMove(move_spline, data);
- unit.SendMessageToSet(&data,true);
+ unit->SendMessageToSet(&data,true);
+
+ return move_spline.Duration();
}
- MoveSplineInit::MoveSplineInit(Unit& m) : unit(m)
+ MoveSplineInit::MoveSplineInit(Unit* m) : unit(m)
{
// Elevators also use MOVEMENTFLAG_ONTRANSPORT but we do not keep track of their position changes
- args.TransformForTransport = unit.HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit.GetTransGUID();
+ args.TransformForTransport = unit->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && unit->GetTransGUID();
// mix existing state into new
- args.flags.walkmode = unit.m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
- args.flags.flying = unit.m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY|MOVEMENTFLAG_DISABLE_GRAVITY));
+ args.flags.walkmode = unit->m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALKING);
+ args.flags.flying = unit->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_DISABLE_GRAVITY));
}
- void MoveSplineInit::SetFacing(const Unit * target)
+ void MoveSplineInit::SetFacing(const Unit* target)
{
args.flags.EnableFacingTarget();
args.facing.target = target->GetGUID();
@@ -136,9 +138,9 @@ namespace Movement
{
if (args.TransformForTransport)
{
- if (Unit* vehicle = unit.GetVehicleBase())
+ if (Unit* vehicle = unit->GetVehicleBase())
angle -= vehicle->GetOrientation();
- else if (Transport* transport = unit.GetTransport())
+ else if (Transport* transport = unit->GetTransport())
angle -= transport->GetOrientation();
}
@@ -146,25 +148,34 @@ namespace Movement
args.flags.EnableFacingAngle();
}
- void MoveSplineInit::MoveTo(Vector3 const& dest)
+ void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination)
{
- args.path_Idx_offset = 0;
- args.path.resize(2);
- TransportPathTransform transform(unit, args.TransformForTransport);
- args.path[1] = transform(dest);
+ if (generatePath)
+ {
+ PathGenerator path(unit);
+ path.CalculatePath(dest.x, dest.y, dest.z, forceDestination);
+ MovebyPath(path.getPath());
+ }
+ else
+ {
+ args.path_Idx_offset = 0;
+ args.path.resize(2);
+ TransportPathTransform transform(unit, args.TransformForTransport);
+ args.path[1] = transform(dest);
+ }
}
Vector3 TransportPathTransform::operator()(Vector3 input)
{
if (_transformForTransport)
{
- if (Unit* vehicle = _owner.GetVehicleBase())
+ if (Unit* vehicle = _owner->GetVehicleBase())
{
input.x -= vehicle->GetPositionX();
input.y -= vehicle->GetPositionY();
input.z -= vehicle->GetPositionZMinusOffset();
}
- else if (Transport* transport = _owner.GetTransport())
+ else if (Transport* transport = _owner->GetTransport())
{
float unused = 0.0f;
transport->CalculatePassengerOffset(input.x, input.y, input.z, unused);
diff --git a/src/server/game/Movement/Spline/MoveSplineInit.h b/src/server/game/Movement/Spline/MoveSplineInit.h
index ef847809ac8..53adec2e633 100644
--- a/src/server/game/Movement/Spline/MoveSplineInit.h
+++ b/src/server/game/Movement/Spline/MoveSplineInit.h
@@ -20,6 +20,7 @@
#define TRINITYSERVER_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
+#include "PathGenerator.h"
class Unit;
@@ -37,12 +38,12 @@ namespace Movement
class TransportPathTransform
{
public:
- TransportPathTransform(Unit& owner, bool transformForTransport)
+ TransportPathTransform(Unit* owner, bool transformForTransport)
: _owner(owner), _transformForTransport(transformForTransport) { }
Vector3 operator()(Vector3 input);
private:
- Unit& _owner;
+ Unit* _owner;
bool _transformForTransport;
};
@@ -52,11 +53,11 @@ namespace Movement
{
public:
- explicit MoveSplineInit(Unit& m);
+ explicit MoveSplineInit(Unit* m);
/* Final pass of initialization that launches spline movement.
*/
- void Launch();
+ int32 Launch();
/* Adds movement by parabolic trajectory
* @param amplitude - the maximum height of parabola, value could be negative and positive
@@ -75,7 +76,7 @@ namespace Movement
*/
void SetFacing(float angle);
void SetFacing(Vector3 const& point);
- void SetFacing(const Unit * target);
+ void SetFacing(const Unit* target);
/* Initializes movement by path
* @param path - array of points, shouldn't be empty
@@ -83,10 +84,10 @@ namespace Movement
*/
void MovebyPath(const PointsArray& path, int32 pointId = 0);
- /* Initializes simple A to B mition, A is current unit's position, B is destination
+ /* Initializes simple A to B motion, A is current unit's position, B is destination
*/
- void MoveTo(const Vector3& destination);
- void MoveTo(float x, float y, float z);
+ void MoveTo(const Vector3& destination, bool generatePath = true, bool forceDestination = false);
+ void MoveTo(float x, float y, float z, bool generatePath = true, bool forceDestination = false);
/* Sets Id of fisrt point of the path. When N-th path point will be done ILisener will notify that pointId + N done
* Needed for waypoint movement where path splitten into parts
@@ -137,7 +138,7 @@ namespace Movement
protected:
MoveSplineInitArgs args;
- Unit& unit;
+ Unit* unit;
};
inline void MoveSplineInit::SetFly() { args.flags.EnableFlying(); }
@@ -158,10 +159,10 @@ namespace Movement
std::transform(controls.begin(), controls.end(), args.path.begin(), TransportPathTransform(unit, args.TransformForTransport));
}
- inline void MoveSplineInit::MoveTo(float x, float y, float z)
+ inline void MoveSplineInit::MoveTo(float x, float y, float z, bool generatePath, bool forceDestination)
{
Vector3 v(x, y, z);
- MoveTo(v);
+ MoveTo(v, generatePath, forceDestination);
}
inline void MoveSplineInit::SetParabolic(float amplitude, float time_shift)
diff --git a/src/server/game/Movement/Waypoints/Path.h b/src/server/game/Movement/Waypoints/Path.h
index b6ddaa9d726..038958593fb 100755
--- a/src/server/game/Movement/Waypoints/Path.h
+++ b/src/server/game/Movement/Waypoints/Path.h
@@ -20,10 +20,12 @@
#define TRINITYCORE_PATH_H
#include "Common.h"
-#include <vector>
+#include <deque>
-struct SimplePathNode
+struct PathNode
{
+ PathNode(): x(0.0f), y(0.0f), z(0.0f) { }
+ PathNode(float _x, float _y, float _z): x(_x), y(_y), z(_z) { }
float x, y, z;
};
template<typename PathElem, typename PathNode = PathElem>
@@ -36,6 +38,20 @@ class Path
void resize(unsigned int sz) { i_nodes.resize(sz); }
void clear() { i_nodes.clear(); }
void erase(uint32 idx) { i_nodes.erase(i_nodes.begin()+idx); }
+ void crop(unsigned int start, unsigned int end)
+ {
+ while(start && !i_nodes.empty())
+ {
+ i_nodes.pop_front();
+ --start;
+ }
+
+ while(end && !i_nodes.empty())
+ {
+ i_nodes.pop_back();
+ --end;
+ }
+ }
float GetTotalLength(uint32 start, uint32 end) const
{
@@ -76,10 +92,10 @@ class Path
void set(size_t idx, PathElem elem) { i_nodes[idx] = elem; }
protected:
- std::vector<PathElem> i_nodes;
+ std::deque<PathElem> i_nodes;
};
-typedef Path<SimplePathNode> SimplePath;
+typedef Path<PathNode> SimplePath;
#endif
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index ea74b6d06dd..870533d2aca 100755
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -21,6 +21,7 @@
*/
#include "Common.h"
+#include "Memory.h"
#include "DatabaseEnv.h"
#include "Config.h"
#include "SystemConfig.h"
@@ -54,6 +55,7 @@
#include "TemporarySummon.h"
#include "WaypointMovementGenerator.h"
#include "VMapFactory.h"
+#include "MMapFactory.h"
#include "GameEventMgr.h"
#include "PoolMgr.h"
#include "GridNotifiersImpl.h"
@@ -135,6 +137,7 @@ World::~World()
delete command;
VMAP::VMapFactory::clear();
+ MMAP::MMapFactory::clear();
//TODO free addSessQueue
}
@@ -1121,6 +1124,10 @@ void World::LoadConfigSettings(bool reload)
sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Using DataDir %s", m_dataPath.c_str());
}
+ m_bool_configs[CONFIG_ENABLE_MMAPS] = ConfigMgr::GetBoolDefault("mmap.enablePathFinding", true);
+ sLog->outInfo(LOG_FILTER_SERVER_LOADING, "WORLD: MMap data directory is: %smmaps", m_dataPath.c_str());
+ MMAP::MMapFactory::preventPathfindingOnMaps(ConfigMgr::GetStringDefault("mmap.ignoreMapIds", "").c_str());
+
m_bool_configs[CONFIG_VMAP_INDOOR_CHECK] = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", 0);
bool enableIndoor = ConfigMgr::GetBoolDefault("vmap.enableIndoorCheck", true);
bool enableLOS = ConfigMgr::GetBoolDefault("vmap.enableLOS", true);
@@ -1225,6 +1232,9 @@ void World::SetInitialWorldSettings()
///- Initialize the random number generator
srand((unsigned int)time(NULL));
+ ///- Initialize detour memory management
+ dtAllocSetCustom(dtCustomAlloc, dtCustomFree);
+
///- Initialize config settings
LoadConfigSettings();
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index a6cdd4742f6..8e97e99fa3f 100755..100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -164,6 +164,7 @@ enum WorldBoolConfigs
CONFIG_QUEST_IGNORE_AUTO_ACCEPT,
CONFIG_QUEST_IGNORE_AUTO_COMPLETE,
CONFIG_WARDEN_ENABLED,
+ CONFIG_ENABLE_MMAPS,
CONFIG_WINTERGRASP_ENABLE,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index f148ae2b3ee..619e2730f79 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -51,6 +51,8 @@ message("")
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/zlib
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
index 7ee67060f97..c6b89d37878 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
@@ -342,7 +342,7 @@ class boss_algalon_the_observer : public CreatureScript
DoCast(me, SPELL_RIDE_THE_LIGHTNING, true);
me->GetMotionMaster()->MovePoint(POINT_ALGALON_LAND, AlgalonLandPos);
me->SetHomePosition(AlgalonLandPos);
- Movement::MoveSplineInit init(*me);
+ Movement::MoveSplineInit init(me);
init.MoveTo(AlgalonLandPos.GetPositionX(), AlgalonLandPos.GetPositionY(), AlgalonLandPos.GetPositionZ());
init.SetOrientationFixed(true);
init.Launch();
diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt
index de998442419..e016ad249b3 100644
--- a/src/server/shared/CMakeLists.txt
+++ b/src/server/shared/CMakeLists.txt
@@ -52,6 +52,7 @@ set(shared_STAT_SRCS
include_directories(
${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/SFMT
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/utf8cpp
diff --git a/src/server/shared/Memory.h b/src/server/shared/Memory.h
new file mode 100644
index 00000000000..ac697f7a1af
--- /dev/null
+++ b/src/server/shared/Memory.h
@@ -0,0 +1,17 @@
+
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+// memory management
+inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/)
+{
+ return (void*)new unsigned char[size];
+}
+
+inline void dtCustomFree(void* ptr)
+{
+ delete [] (unsigned char*)ptr;
+}
+
+#endif \ No newline at end of file
diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index eb5bb04d766..45442123258 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -45,6 +45,7 @@ endif()
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/dep/gsoap
${CMAKE_SOURCE_DIR}/dep/sockets/include
${CMAKE_SOURCE_DIR}/dep/SFMT
@@ -166,6 +167,7 @@ target_link_libraries(worldserver
collision
g3dlib
gsoap
+ Detour
${JEMALLOC_LIBRARY}
${READLINE_LIBRARY}
${TERMCAP_LIBRARY}
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 9eab3c40a2d..1a237eac08a 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -268,6 +268,21 @@ PlayerSave.Stats.MinLevel = 0
PlayerSave.Stats.SaveOnlyOnLogout = 1
#
+# mmap.enablePathFinding
+# Description: Enable/Disable pathfinding using mmaps
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+mmap.enablePathFinding = 1
+
+#
+# mmap.ignoreMapIds
+# Disable mmap pathfinding on the listed maps.
+# List of map ids with delimiter ','
+
+mmap.ignoreMapIds = ""
+
+#
# vmap.enableLOS
# vmap.enableHeight
# Description: VMmap support for line of sight and height calculation.
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index a956655ffaa..2d378966aff 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -11,3 +11,4 @@
add_subdirectory(map_extractor)
add_subdirectory(vmap4_assembler)
add_subdirectory(vmap4_extractor)
+add_subdirectory(mmaps_generator)
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index f474925313e..021b22fd70c 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -276,7 +276,7 @@ void ReadLiquidTypeTableDBC()
// Map file format data
static char const* MAP_MAGIC = "MAPS";
-static char const* MAP_VERSION_MAGIC = "v1.2";
+static char const* MAP_VERSION_MAGIC = "v1.3";
static char const* MAP_AREA_MAGIC = "AREA";
static char const* MAP_HEIGHT_MAGIC = "MHGT";
static char const* MAP_LIQUID_MAGIC = "MLIQ";
@@ -292,6 +292,8 @@ struct map_fileheader
uint32 heightMapSize;
uint32 liquidMapOffset;
uint32 liquidMapSize;
+ uint32 holesOffset;
+ uint32 holesSize;
};
#define MAP_AREA_NO_AREA 0x0001
@@ -826,9 +828,38 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.liquidMapSize += sizeof(float)*liquidHeader.width*liquidHeader.height;
}
+ // map hole info
+ uint16 holes[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
+
+ if (map.liquidMapOffset)
+ map.holesOffset = map.liquidMapOffset + map.liquidMapSize;
+ else
+ map.holesOffset = map.heightMapOffset + map.heightMapSize;
+
+ memset(holes, 0, sizeof(holes));
+ bool hasHoles = false;
+
+ for (int i = 0; i < ADT_CELLS_PER_GRID; ++i)
+ {
+ for (int j = 0; j < ADT_CELLS_PER_GRID; ++j)
+ {
+ adt_MCNK * cell = cells->getMCNK(i,j);
+ if (!cell)
+ continue;
+ holes[i][j] = cell->holes;
+ if (!hasHoles && cell->holes != 0)
+ hasHoles = true;
+ }
+ }
+
+ if (hasHoles)
+ map.holesSize = sizeof(holes);
+ else
+ map.holesSize = 0;
+
// Ok all data prepared - store it
- FILE *output=fopen(filename2, "wb");
- if(!output)
+ FILE* output = fopen(filename2, "wb");
+ if (!output)
{
printf("Can't create the output file '%s'\n", filename2);
return false;
@@ -875,6 +906,11 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
fwrite(&liquid_height[y+liquidHeader.offsetY][liquidHeader.offsetX], sizeof(float), liquidHeader.width, output);
}
}
+
+ // store hole data
+ if (hasHoles)
+ fwrite(holes, map.holesSize, 1, output);
+
fclose(output);
return true;
diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt
new file mode 100644
index 00000000000..46bf9d00d43
--- /dev/null
+++ b/src/tools/mmaps_generator/CMakeLists.txt
@@ -0,0 +1,68 @@
+# Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+file(GLOB_RECURSE sources *.cpp *.h)
+
+# definitions
+add_definitions(-DNO_CORE_FUNCS)
+add_definitions(-DDEBUG)
+add_definitions(-DNO_vsnprintf)
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${ACE_INCLUDE_DIR}
+ ${MYSQL_INCLUDE_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/dep/zlib
+ ${CMAKE_SOURCE_DIR}/dep/bzip2
+ ${CMAKE_SOURCE_DIR}/dep/acelite
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Database/Implementation
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Threading
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Logging
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
+ ${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
+ ${CMAKE_SOURCE_DIR}/src/server/game/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/game/DataStores
+ ${CMAKE_SOURCE_DIR}/src/server/game/Movement/Waypoints
+ ${CMAKE_SOURCE_DIR}/src/server/game/Grids
+ ${CMAKE_SOURCE_DIR}/src/server/game/Grids/Cells
+ ${CMAKE_SOURCE_DIR}/src/server/game/Miscellaneous
+ ${CMAKE_SOURCE_DIR}/src/server/game/Conditions
+ ${CMAKE_SOURCE_DIR}/src/server/collision
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Management
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Maps
+ ${CMAKE_SOURCE_DIR}/src/server/collision/Models
+)
+
+add_executable(mmaps_generator ${sources})
+
+target_link_libraries(mmaps_generator
+ ${MYSQL_LIBRARY}
+ ${ACE_LIBRARY}
+ ${BZIP2_LIBRARIES}
+ ${ZLIB_LIBRARIES}
+ Recast
+ Detour
+ collision
+ g3dlib
+ shared
+)
+
+if( UNIX )
+ install(TARGETS mmaps_generator DESTINATION bin)
+elseif( WIN32 )
+ install(TARGETS mmaps_generator DESTINATION "${CMAKE_INSTALL_PREFIX}")
+endif()
diff --git a/src/tools/mmaps_generator/Info/readme.txt b/src/tools/mmaps_generator/Info/readme.txt
new file mode 100644
index 00000000000..8d7c4f9d2e0
--- /dev/null
+++ b/src/tools/mmaps_generator/Info/readme.txt
@@ -0,0 +1,66 @@
+Generator command line args
+
+--offMeshInput [file.*] Path to file containing off mesh connections data.
+ Format must be: (see offmesh_example.txt)
+ "map_id tile_x,tile_y (start_x start_y start_z) (end_x end_y end_z) size //optional comments"
+ Single mesh connection per line.
+
+--silent Make us script friendly. Do not wait for user input
+ on error or completion.
+
+--bigBaseUnit [true|false] Generate tile/map using bigger basic unit.
+ Use this option only if you have unexpected gaps.
+
+ false: use normal metrics (default)
+
+--maxAngle [#] Max walkable inclination angle
+
+ float between 45 and 90 degrees (default 60)
+
+--skipLiquid liquid data for maps
+
+ false: include liquid data (default)
+
+--skipContinents [true|false] continents are maps 0 (Eastern Kingdoms),
+ 1 (Kalimdor), 530 (Outlands), 571 (Northrend)
+
+ false: build continents (default)
+
+--skipJunkMaps [true|false] junk maps include some unused
+ maps, transport maps, and some other
+
+ true: skip junk maps (default)
+
+--skipBattlegrounds [true|false] does not include PVP arenas
+
+ false: skip battlegrounds (default)
+
+--debugOutput [true|false] create debugging files for use with RecastDemo
+ if you are only creating mmaps for use with MaNGOS,
+ you don't want debugging files
+
+ false: don't create debugging files (default)
+
+--tile [#,#] Build the specified tile
+ seperate number with a comma ','
+ must specify a map number (see below)
+ if this option is not used, all tiles are built
+
+ [#] Build only the map specified by #
+ this command will build the map regardless of --skip* option settings
+ if you do not specify a map number, builds all maps that pass the filters specified by --skip* options
+
+
+examples:
+
+movement_extractor
+builds maps using the default settings (see above for defaults)
+
+movement_extractor --skipContinents true
+builds the default maps, except continents
+
+movement_extractor 0
+builds all tiles of map 0
+
+movement_extractor 0 --tile 34,46
+builds only tile 34,46 of map 0 (this is the southern face of blackrock mountain) \ No newline at end of file
diff --git a/src/tools/mmaps_generator/IntermediateValues.cpp b/src/tools/mmaps_generator/IntermediateValues.cpp
new file mode 100644
index 00000000000..b473d6472a0
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#include "IntermediateValues.h"
+
+namespace MMAP
+{
+ IntermediateValues::~IntermediateValues()
+ {
+ rcFreeCompactHeightfield(compactHeightfield);
+ rcFreeHeightField(heightfield);
+ rcFreeContourSet(contours);
+ rcFreePolyMesh(polyMesh);
+ rcFreePolyMeshDetail(polyMeshDetail);
+ }
+
+ void IntermediateValues::writeIV(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileX, tileY);
+
+ printf("%sWriting debug output... \r", tileString);
+
+ string name("meshes/%03u%02i%02i.");
+
+#define DEBUG_WRITE(fileExtension,data) \
+ do { \
+ sprintf(fileName, (name + fileExtension).c_str(), mapID, tileY, tileX); \
+ FILE* file = fopen(fileName, "wb"); \
+ if (!file) \
+ { \
+ char message[1024]; \
+ sprintf(message, "%sFailed to open %s for writing!\n", tileString, fileName); \
+ perror(message); \
+ } \
+ else \
+ debugWrite(file, data); \
+ if (file) fclose(file); \
+ printf("%sWriting debug output... \r", tileString); \
+ } while (false)
+
+ if (heightfield)
+ DEBUG_WRITE("hf", heightfield);
+ if (compactHeightfield)
+ DEBUG_WRITE("chf", compactHeightfield);
+ if (contours)
+ DEBUG_WRITE("cs", contours);
+ if (polyMesh)
+ DEBUG_WRITE("pmesh", polyMesh);
+ if (polyMeshDetail)
+ DEBUG_WRITE("dmesh", polyMeshDetail);
+
+#undef DEBUG_WRITE
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcHeightfield* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->width), sizeof(int), 1, file);
+ fwrite(&(mesh->height), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+
+ for (int y = 0; y < mesh->height; ++y)
+ for (int x = 0; x < mesh->width; ++x)
+ {
+ rcSpan* span = mesh->spans[x+y*mesh->width];
+
+ // first, count the number of spans
+ int spanCount = 0;
+ while (span)
+ {
+ spanCount++;
+ span = span->next;
+ }
+
+ // write the span count
+ fwrite(&spanCount, sizeof(int), 1, file);
+
+ // write the spans
+ span = mesh->spans[x+y*mesh->width];
+ while (span)
+ {
+ fwrite(span, sizeof(rcSpan), 1, file);
+ span = span->next;
+ }
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcCompactHeightfield* chf)
+ {
+ if (!file | !chf)
+ return;
+
+ fwrite(&(chf->width), sizeof(chf->width), 1, file);
+ fwrite(&(chf->height), sizeof(chf->height), 1, file);
+ fwrite(&(chf->spanCount), sizeof(chf->spanCount), 1, file);
+
+ fwrite(&(chf->walkableHeight), sizeof(chf->walkableHeight), 1, file);
+ fwrite(&(chf->walkableClimb), sizeof(chf->walkableClimb), 1, file);
+
+ fwrite(&(chf->maxDistance), sizeof(chf->maxDistance), 1, file);
+ fwrite(&(chf->maxRegions), sizeof(chf->maxRegions), 1, file);
+
+ fwrite(chf->bmin, sizeof(chf->bmin), 1, file);
+ fwrite(chf->bmax, sizeof(chf->bmax), 1, file);
+
+ fwrite(&(chf->cs), sizeof(chf->cs), 1, file);
+ fwrite(&(chf->ch), sizeof(chf->ch), 1, file);
+
+ int tmp = 0;
+ if (chf->cells) tmp |= 1;
+ if (chf->spans) tmp |= 2;
+ if (chf->dist) tmp |= 4;
+ if (chf->areas) tmp |= 8;
+
+ fwrite(&tmp, sizeof(tmp), 1, file);
+
+ if (chf->cells)
+ fwrite(chf->cells, sizeof(rcCompactCell), chf->width*chf->height, file);
+ if (chf->spans)
+ fwrite(chf->spans, sizeof(rcCompactSpan), chf->spanCount, file);
+ if (chf->dist)
+ fwrite(chf->dist, sizeof(unsigned short), chf->spanCount, file);
+ if (chf->areas)
+ fwrite(chf->areas, sizeof(unsigned char), chf->spanCount, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcContourSet* cs)
+ {
+ if (!file || !cs)
+ return;
+
+ fwrite(&(cs->cs), sizeof(float), 1, file);
+ fwrite(&(cs->ch), sizeof(float), 1, file);
+ fwrite(cs->bmin, sizeof(float), 3, file);
+ fwrite(cs->bmax, sizeof(float), 3, file);
+ fwrite(&(cs->nconts), sizeof(int), 1, file);
+ for (int i = 0; i < cs->nconts; ++i)
+ {
+ fwrite(&cs->conts[i].area, sizeof(unsigned char), 1, file);
+ fwrite(&cs->conts[i].reg, sizeof(unsigned short), 1, file);
+ fwrite(&cs->conts[i].nverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].verts, sizeof(int), cs->conts[i].nverts*4, file);
+ fwrite(&cs->conts[i].nrverts, sizeof(int), 1, file);
+ fwrite(cs->conts[i].rverts, sizeof(int), cs->conts[i].nrverts*4, file);
+ }
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMesh* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->cs), sizeof(float), 1, file);
+ fwrite(&(mesh->ch), sizeof(float), 1, file);
+ fwrite(&(mesh->nvp), sizeof(int), 1, file);
+ fwrite(mesh->bmin, sizeof(float), 3, file);
+ fwrite(mesh->bmax, sizeof(float), 3, file);
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(unsigned short), mesh->nverts*3, file);
+ fwrite(&(mesh->npolys), sizeof(int), 1, file);
+ fwrite(mesh->polys, sizeof(unsigned short), mesh->npolys*mesh->nvp*2, file);
+ fwrite(mesh->flags, sizeof(unsigned short), mesh->npolys, file);
+ fwrite(mesh->areas, sizeof(unsigned char), mesh->npolys, file);
+ fwrite(mesh->regs, sizeof(unsigned short), mesh->npolys, file);
+ }
+
+ void IntermediateValues::debugWrite(FILE* file, const rcPolyMeshDetail* mesh)
+ {
+ if (!file || !mesh)
+ return;
+
+ fwrite(&(mesh->nverts), sizeof(int), 1, file);
+ fwrite(mesh->verts, sizeof(float), mesh->nverts*3, file);
+ fwrite(&(mesh->ntris), sizeof(int), 1, file);
+ fwrite(mesh->tris, sizeof(char), mesh->ntris*4, file);
+ fwrite(&(mesh->nmeshes), sizeof(int), 1, file);
+ fwrite(mesh->meshes, sizeof(int), mesh->nmeshes*4, file);
+ }
+
+ void IntermediateValues::generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ char objFileName[255];
+ sprintf(objFileName, "meshes/map%03u%02u%02u.obj", mapID, tileY, tileX);
+
+ FILE* objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ G3D::Array<float> allVerts;
+ G3D::Array<int> allTris;
+
+ allTris.append(meshData.liquidTris);
+ allVerts.append(meshData.liquidVerts);
+ TerrainBuilder::copyIndices(meshData.solidTris, allTris, allVerts.size() / 3);
+ allVerts.append(meshData.solidVerts);
+
+ float* verts = allVerts.getCArray();
+ int vertCount = allVerts.size() / 3;
+ int* tris = allTris.getCArray();
+ int triCount = allTris.size() / 3;
+
+ for (int i = 0; i < allVerts.size() / 3; i++)
+ fprintf(objFile, "v %f %f %f\n", verts[i*3], verts[i*3 + 1], verts[i*3 + 2]);
+
+ for (int i = 0; i < allTris.size() / 3; i++)
+ fprintf(objFile, "f %i %i %i\n", tris[i*3] + 1, tris[i*3 + 1] + 1, tris[i*3 + 2] + 1);
+
+ fclose(objFile);
+
+
+ char tileString[25];
+ sprintf(tileString, "[%02u,%02u]: ", tileY, tileX);
+ printf("%sWriting debug output... \r", tileString);
+
+ sprintf(objFileName, "meshes/%03u.map", mapID);
+
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ char b = '\0';
+ fwrite(&b, sizeof(char), 1, objFile);
+ fclose(objFile);
+
+ sprintf(objFileName, "meshes/%03u%02u%02u.mesh", mapID, tileY, tileX);
+ objFile = fopen(objFileName, "wb");
+ if (!objFile)
+ {
+ char message[1024];
+ sprintf(message, "Failed to open %s for writing!\n", objFileName);
+ perror(message);
+ return;
+ }
+
+ fwrite(&vertCount, sizeof(int), 1, objFile);
+ fwrite(verts, sizeof(float), vertCount*3, objFile);
+ fflush(objFile);
+
+ fwrite(&triCount, sizeof(int), 1, objFile);
+ fwrite(tris, sizeof(int), triCount*3, objFile);
+ fflush(objFile);
+
+ fclose(objFile);
+ }
+}
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
new file mode 100644
index 00000000000..a267a0f6412
--- /dev/null
+++ b/src/tools/mmaps_generator/IntermediateValues.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#ifndef _INTERMEDIATE_VALUES_H
+#define _INTERMEDIATE_VALUES_H
+
+#include "PathCommon.h"
+#include "TerrainBuilder.h"
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+namespace MMAP
+{
+ // this class gathers all debug info holding and output
+ struct IntermediateValues
+ {
+ rcHeightfield* heightfield;
+ rcCompactHeightfield* compactHeightfield;
+ rcContourSet* contours;
+ rcPolyMesh* polyMesh;
+ rcPolyMeshDetail* polyMeshDetail;
+
+ IntermediateValues() : compactHeightfield(NULL), heightfield(NULL),
+ contours(NULL), polyMesh(NULL), polyMeshDetail(NULL) {}
+ ~IntermediateValues();
+
+ void writeIV(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ void debugWrite(FILE* file, const rcHeightfield* mesh);
+ void debugWrite(FILE* file, const rcCompactHeightfield* chf);
+ void debugWrite(FILE* file, const rcContourSet* cs);
+ void debugWrite(FILE* file, const rcPolyMesh* mesh);
+ void debugWrite(FILE* file, const rcPolyMeshDetail* mesh);
+
+ void generateObjFile(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ };
+}
+#endif
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
new file mode 100644
index 00000000000..2a74233a1a7
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -0,0 +1,970 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "MapTree.h"
+#include "ModelInstance.h"
+#include "LoginDatabase.h"
+
+#include "DetourNavMeshBuilder.h"
+#include "DetourNavMesh.h"
+#include "DetourCommon.h"
+
+// These make the linker happy.
+LoginDatabaseWorkerPool LoginDatabase;
+uint32 GetLiquidFlags(uint32 liquidType)
+{
+ return 0;
+}
+
+#include "DisableMgr.h"
+namespace DisableMgr
+{
+ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags)
+ {
+ return 0;
+ }
+}
+
+using namespace VMAP;
+
+namespace MMAP
+{
+ MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid,
+ bool skipContinents, bool skipJunkMaps, bool skipBattlegrounds,
+ bool debugOutput, bool bigBaseUnit, const char* offMeshFilePath) :
+ m_terrainBuilder(NULL),
+ m_debugOutput (debugOutput),
+ m_skipContinents (skipContinents),
+ m_skipJunkMaps (skipJunkMaps),
+ m_skipBattlegrounds (skipBattlegrounds),
+ m_maxWalkableAngle (maxWalkableAngle),
+ m_bigBaseUnit (bigBaseUnit),
+ m_rcContext (NULL),
+ m_offMeshFilePath (offMeshFilePath)
+ {
+ m_terrainBuilder = new TerrainBuilder(skipLiquid);
+
+ m_rcContext = new rcContext(false);
+
+ discoverTiles();
+ }
+
+ /**************************************************************************/
+ MapBuilder::~MapBuilder()
+ {
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ (*it).second->clear();
+ delete (*it).second;
+ }
+
+ delete m_terrainBuilder;
+ delete m_rcContext;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::discoverTiles()
+ {
+ vector<string> files;
+ uint32 mapID, tileX, tileY, tileID, count = 0;
+ char filter[12];
+
+ printf("Discovering maps... ");
+ getDirContents(files, "maps");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ if (m_tiles.find(mapID) == m_tiles.end())
+ {
+ m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>));
+ count++;
+ }
+ }
+
+ files.clear();
+ getDirContents(files, "vmaps", "*.vmtree");
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ mapID = uint32(atoi(files[i].substr(0,3).c_str()));
+ m_tiles.insert(pair<uint32,set<uint32>*>(mapID, new set<uint32>));
+ count++;
+ }
+ printf("found %u.\n", count);
+
+ count = 0;
+ printf("Discovering tiles... ");
+ for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
+ {
+ set<uint32>* tiles = (*itr).second;
+ mapID = (*itr).first;
+
+ sprintf(filter, "%03u*.vmtile", mapID);
+ files.clear();
+ getDirContents(files, "vmaps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileX = uint32(atoi(files[i].substr(7,2).c_str()));
+ tileY = uint32(atoi(files[i].substr(4,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileY, tileX);
+
+ tiles->insert(tileID);
+ count++;
+ }
+
+ sprintf(filter, "%03u*", mapID);
+ files.clear();
+ getDirContents(files, "maps", filter);
+ for (uint32 i = 0; i < files.size(); ++i)
+ {
+ tileY = uint32(atoi(files[i].substr(3,2).c_str()));
+ tileX = uint32(atoi(files[i].substr(5,2).c_str()));
+ tileID = StaticMapTree::packTileID(tileX, tileY);
+
+ if (tiles->insert(tileID).second)
+ count++;
+ }
+ }
+ printf("found %u.\n\n", count);
+ }
+
+ /**************************************************************************/
+ set<uint32>* MapBuilder::getTileList(uint32 mapID)
+ {
+ TileList::iterator itr = m_tiles.find(mapID);
+ if (itr != m_tiles.end())
+ return (*itr).second;
+
+ set<uint32>* tiles = new set<uint32>();
+ m_tiles.insert(pair<uint32, set<uint32>*>(mapID, tiles));
+ return tiles;
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildAllMaps(int threads)
+ {
+ std::vector<BuilderThread*> _threads;
+
+ for (int i = 0; i < threads; ++i)
+ _threads.push_back(new BuilderThread(this));
+
+ for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
+ {
+ uint32 mapID = it->first;
+ if (!shouldSkipMap(mapID))
+ {
+ if (threads > 1)
+ {
+ bool next = false;
+ while (!next)
+ {
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ if ((*_th)->Free)
+ {
+ (*_th)->SetMapId(mapID);
+ (*_th)->activate();
+ next = true;
+ break;
+ }
+ }
+ // Wait for 20 seconds
+ ACE_OS::sleep(ACE_Time_Value (0, 20000));
+ }
+ }
+ else
+ buildMap(mapID);
+ }
+ }
+
+ // Free memory
+ for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th)
+ {
+ (*_th)->wait();
+ delete *_th;
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
+ {
+ maxX = INT_MAX;
+ maxY = INT_MAX;
+ minX = INT_MIN;
+ minY = INT_MIN;
+
+ float bmin[3], bmax[3], lmin[3], lmax[3];
+ MeshData meshData;
+
+ // make sure we process maps which don't have tiles
+ // initialize the static tree, which loads WDT models
+ if (!m_terrainBuilder->loadVMap(mapID, 64, 64, meshData))
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() + meshData.liquidVerts.size() == 0)
+ return;
+
+ // get the coord bounds of the model data
+ if (meshData.solidVerts.size() && meshData.liquidVerts.size())
+ {
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+ rcVmin(bmin, lmin);
+ rcVmax(bmax, lmax);
+ }
+ else if (meshData.solidVerts.size())
+ rcCalcBounds(meshData.solidVerts.getCArray(), meshData.solidVerts.size() / 3, bmin, bmax);
+ else
+ rcCalcBounds(meshData.liquidVerts.getCArray(), meshData.liquidVerts.size() / 3, lmin, lmax);
+
+ // convert coord bounds to grid bounds
+ maxX = 32 - bmin[0] / GRID_SIZE;
+ maxY = 32 - bmin[2] / GRID_SIZE;
+ minX = 32 - bmax[0] / GRID_SIZE;
+ minY = 32 - bmax[2] / GRID_SIZE;
+ }
+
+ void MapBuilder::buildMeshFromFile(char* name)
+ {
+ FILE* file = fopen(name, "rb");
+ if (!file)
+ return;
+
+ printf("Building mesh from file\n");
+ int tileX, tileY, mapId;
+ fread(&mapId, sizeof(int), 1, file);
+ fread(&tileX, sizeof(int), 1, file);
+ fread(&tileY, sizeof(int), 1, file);
+
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapId, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ fclose(file);
+ return;
+ }
+
+
+ int verticesCount, indicesCount;
+ fread(&verticesCount, sizeof(int), 1, file);
+ fread(&indicesCount, sizeof(int), 1, file);
+
+ float* verts = new float[verticesCount];
+ int* inds = new int[indicesCount];
+
+ fread(verts, sizeof(float), verticesCount, file);
+ fread(inds, sizeof(int), indicesCount, file);
+
+ MeshData data;
+
+ for (int i = 0; i < verticesCount; ++i)
+ data.solidVerts.append(verts[i]);
+
+ for (int i = 0; i < indicesCount; ++i)
+ data.solidTris.append(inds[i]);
+
+ TerrainBuilder::cleanVertices(data.solidVerts, data.solidTris);
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, data.solidVerts.getCArray(), data.solidVerts.size() / 3, bmin, bmax);
+
+ // build navmesh tile
+ buildMoveMapTile(mapId, tileX, tileY, data, bmin, bmax, navMesh);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("Failed creating navmesh! \n");
+ return;
+ }
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ dtFreeNavMesh(navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMap(uint32 mapID)
+ {
+ printf("[Thread %i] Building map %03u:\n", ACE_Thread::self(), mapID);
+
+ set<uint32>* tiles = getTileList(mapID);
+
+ // make sure we process maps which don't have tiles
+ if (!tiles->size())
+ {
+ // convert coord bounds to grid bounds
+ uint32 minX, minY, maxX, maxY;
+ getGridBounds(mapID, minX, minY, maxX, maxY);
+
+ // add all tiles within bounds to tile list.
+ for (uint32 i = minX; i <= maxX; ++i)
+ for (uint32 j = minY; j <= maxY; ++j)
+ tiles->insert(StaticMapTree::packTileID(i, j));
+ }
+
+ if (!tiles->empty())
+ {
+ // build navMesh
+ dtNavMesh* navMesh = NULL;
+ buildNavMesh(mapID, navMesh);
+ if (!navMesh)
+ {
+ printf("[Map %i] Failed creating navmesh!\n", mapID);
+ return;
+ }
+
+ // now start building mmtiles for each tile
+ printf("[Map %i] We have %u tiles. \n", mapID, (unsigned int)tiles->size());
+ for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ uint32 tileX, tileY;
+
+ // unpack tile coords
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (shouldSkipTile(mapID, tileX, tileY))
+ continue;
+
+ buildTile(mapID, tileX, tileY, navMesh);
+ }
+
+ dtFreeNavMesh(navMesh);
+ }
+
+ printf("[Map %i] Complete!\n", mapID);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh)
+ {
+ printf("[Map %i] Building tile [%02u,%02u]\n", mapID, tileX, tileY);
+
+ MeshData meshData;
+
+ // get heightmap data
+ m_terrainBuilder->loadMap(mapID, tileX, tileY, meshData);
+
+ // get model data
+ m_terrainBuilder->loadVMap(mapID, tileY, tileX, meshData);
+
+ // if there is no data, give up now
+ if (!meshData.solidVerts.size() && !meshData.liquidVerts.size())
+ return;
+
+ // remove unused vertices
+ TerrainBuilder::cleanVertices(meshData.solidVerts, meshData.solidTris);
+ TerrainBuilder::cleanVertices(meshData.liquidVerts, meshData.liquidTris);
+
+ // gather all mesh data for final data check, and bounds calculation
+ G3D::Array<float> allVerts;
+ allVerts.append(meshData.liquidVerts);
+ allVerts.append(meshData.solidVerts);
+
+ if (!allVerts.size())
+ return;
+
+ // get bounds of current tile
+ float bmin[3], bmax[3];
+ getTileBounds(tileX, tileY, allVerts.getCArray(), allVerts.size() / 3, bmin, bmax);
+
+ m_terrainBuilder->loadOffMeshConnections(mapID, tileX, tileY, meshData, m_offMeshFilePath);
+
+ // build navmesh tile
+ buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildNavMesh(uint32 mapID, dtNavMesh* &navMesh)
+ {
+ set<uint32>* tiles = getTileList(mapID);
+
+ // old code for non-statically assigned bitmask sizes:
+ ///*** calculate number of bits needed to store tiles & polys ***/
+ //int tileBits = dtIlog2(dtNextPow2(tiles->size()));
+ //if (tileBits < 1) tileBits = 1; // need at least one bit!
+ //int polyBits = sizeof(dtPolyRef)*8 - SALT_MIN_BITS - tileBits;
+
+ int tileBits = STATIC_TILE_BITS;
+ int polyBits = STATIC_POLY_BITS;
+
+ int maxTiles = tiles->size();
+ int maxPolysPerTile = 1 << polyBits;
+
+ /*** calculate bounds of map ***/
+
+ uint32 tileXMin = 64, tileYMin = 64, tileXMax = 0, tileYMax = 0, tileX, tileY;
+ for (set<uint32>::iterator it = tiles->begin(); it != tiles->end(); ++it)
+ {
+ StaticMapTree::unpackTileID((*it), tileX, tileY);
+
+ if (tileX > tileXMax)
+ tileXMax = tileX;
+ else if (tileX < tileXMin)
+ tileXMin = tileX;
+
+ if (tileY > tileYMax)
+ tileYMax = tileY;
+ else if (tileY < tileYMin)
+ tileYMin = tileY;
+ }
+
+ // use Max because '32 - tileX' is negative for values over 32
+ float bmin[3], bmax[3];
+ getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax);
+
+ /*** now create the navmesh ***/
+
+ // navmesh creation params
+ dtNavMeshParams navMeshParams;
+ memset(&navMeshParams, 0, sizeof(dtNavMeshParams));
+ navMeshParams.tileWidth = GRID_SIZE;
+ navMeshParams.tileHeight = GRID_SIZE;
+ rcVcopy(navMeshParams.orig, bmin);
+ navMeshParams.maxTiles = maxTiles;
+ navMeshParams.maxPolys = maxPolysPerTile;
+
+ navMesh = dtAllocNavMesh();
+ printf("[Map %i] Creating navMesh...\n", mapID);
+ if (!navMesh->init(&navMeshParams))
+ {
+ printf("[Map %i] Failed creating navmesh! \n", mapID);
+ return;
+ }
+
+ char fileName[25];
+ sprintf(fileName, "mmaps/%03u.mmap", mapID);
+
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ dtFreeNavMesh(navMesh);
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ return;
+ }
+
+ // now that we know navMesh params are valid, we can write them to file
+ fwrite(&navMeshParams, sizeof(dtNavMeshParams), 1, file);
+ fclose(file);
+ }
+
+ /**************************************************************************/
+ void MapBuilder::buildMoveMapTile(uint32 mapID, uint32 tileX, uint32 tileY,
+ MeshData &meshData, float bmin[3], float bmax[3],
+ dtNavMesh* navMesh)
+ {
+ // console output
+ char tileString[20];
+ sprintf(tileString, "[Map %03i] [%02i,%02i]: ", mapID, tileX, tileY);
+ printf("%s Building movemap tiles...\n", tileString);
+
+ IntermediateValues iv;
+
+ float* tVerts = meshData.solidVerts.getCArray();
+ int tVertCount = meshData.solidVerts.size() / 3;
+ int* tTris = meshData.solidTris.getCArray();
+ int tTriCount = meshData.solidTris.size() / 3;
+
+ float* lVerts = meshData.liquidVerts.getCArray();
+ int lVertCount = meshData.liquidVerts.size() / 3;
+ int* lTris = meshData.liquidTris.getCArray();
+ int lTriCount = meshData.liquidTris.size() / 3;
+ uint8* lTriFlags = meshData.liquidType.getCArray();
+
+ // these are WORLD UNIT based metrics
+ // this are basic unit dimentions
+ // value have to divide GRID_SIZE(533.33333f) ( aka: 0.5333, 0.2666, 0.3333, 0.1333, etc )
+ const static float BASE_UNIT_DIM = m_bigBaseUnit ? 0.533333f : 0.266666f;
+
+ // All are in UNIT metrics!
+ const static int VERTEX_PER_MAP = int(GRID_SIZE/BASE_UNIT_DIM + 0.5f);
+ const static int VERTEX_PER_TILE = m_bigBaseUnit ? 40 : 80; // must divide VERTEX_PER_MAP
+ const static int TILES_PER_MAP = VERTEX_PER_MAP/VERTEX_PER_TILE;
+
+ rcConfig config;
+ memset(&config, 0, sizeof(rcConfig));
+
+ rcVcopy(config.bmin, bmin);
+ rcVcopy(config.bmax, bmax);
+
+ config.maxVertsPerPoly = DT_VERTS_PER_POLYGON;
+ config.cs = BASE_UNIT_DIM;
+ config.ch = BASE_UNIT_DIM;
+ config.walkableSlopeAngle = m_maxWalkableAngle;
+ config.tileSize = VERTEX_PER_TILE;
+ config.walkableRadius = m_bigBaseUnit ? 1 : 2;
+ config.borderSize = config.walkableRadius + 3;
+ config.maxEdgeLen = VERTEX_PER_TILE + 1; //anything bigger than tileSize
+ config.walkableHeight = m_bigBaseUnit ? 3 : 6;
+ config.walkableClimb = m_bigBaseUnit ? 2 : 4; // keep less than walkableHeight
+ config.minRegionArea = rcSqr(60);
+ config.mergeRegionArea = rcSqr(50);
+ config.maxSimplificationError = 2.0f; // eliminates most jagged edges (tinny polygons)
+ config.detailSampleDist = config.cs * 64;
+ config.detailSampleMaxError = config.ch * 2;
+
+ // this sets the dimensions of the heightfield - should maybe happen before border padding
+ rcCalcGridSize(config.bmin, config.bmax, config.cs, &config.width, &config.height);
+
+ // allocate subregions : tiles
+ Tile* tiles = new Tile[TILES_PER_MAP * TILES_PER_MAP];
+
+ // Initialize per tile config.
+ rcConfig tileCfg = config;
+ tileCfg.width = config.tileSize + config.borderSize*2;
+ tileCfg.height = config.tileSize + config.borderSize*2;
+
+ // merge per tile poly and detail meshes
+ rcPolyMesh** pmmerge = new rcPolyMesh*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!pmmerge)
+ {
+ printf("%s alloc pmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[TILES_PER_MAP * TILES_PER_MAP];
+ if (!dmmerge)
+ {
+ printf("%s alloc dmmerge FIALED!\n", tileString);
+ return;
+ }
+
+ int nmerge = 0;
+ // build all tiles
+ for (int y = 0; y < TILES_PER_MAP; ++y)
+ {
+ for (int x = 0; x < TILES_PER_MAP; ++x)
+ {
+ Tile& tile = tiles[x + y * TILES_PER_MAP];
+
+ // Calculate the per tile bounding box.
+ tileCfg.bmin[0] = config.bmin[0] + (x*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmin[2] = config.bmin[2] + (y*config.tileSize - config.borderSize)*config.cs;
+ tileCfg.bmax[0] = config.bmin[0] + ((x+1)*config.tileSize + config.borderSize)*config.cs;
+ tileCfg.bmax[2] = config.bmin[2] + ((y+1)*config.tileSize + config.borderSize)*config.cs;
+
+ // build heightfield
+ tile.solid = rcAllocHeightfield();
+ if (!tile.solid || !rcCreateHeightfield(m_rcContext, *tile.solid, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch))
+ {
+ printf("%s Failed building heightfield! \n", tileString);
+ continue;
+ }
+
+ // mark all walkable tiles, both liquids and solids
+ unsigned char* triFlags = new unsigned char[tTriCount];
+ memset(triFlags, NAV_GROUND, tTriCount*sizeof(unsigned char));
+ rcClearUnwalkableTriangles(m_rcContext, tileCfg.walkableSlopeAngle, tVerts, tVertCount, tTris, tTriCount, triFlags);
+ rcRasterizeTriangles(m_rcContext, tVerts, tVertCount, tTris, triFlags, tTriCount, *tile.solid, config.walkableClimb);
+ delete[] triFlags;
+
+ rcFilterLowHangingWalkableObstacles(m_rcContext, config.walkableClimb, *tile.solid);
+ rcFilterLedgeSpans(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid);
+ rcFilterWalkableLowHeightSpans(m_rcContext, tileCfg.walkableHeight, *tile.solid);
+
+ rcRasterizeTriangles(m_rcContext, lVerts, lVertCount, lTris, lTriFlags, lTriCount, *tile.solid, config.walkableClimb);
+
+ // compact heightfield spans
+ tile.chf = rcAllocCompactHeightfield();
+ if (!tile.chf || !rcBuildCompactHeightfield(m_rcContext, tileCfg.walkableHeight, tileCfg.walkableClimb, *tile.solid, *tile.chf))
+ {
+ printf("%s Failed compacting heightfield! \n", tileString);
+ continue;
+ }
+
+ // build polymesh intermediates
+ if (!rcErodeWalkableArea(m_rcContext, config.walkableRadius, *tile.chf))
+ {
+ printf("%s Failed eroding area! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildDistanceField(m_rcContext, *tile.chf))
+ {
+ printf("%s Failed building distance field! \n", tileString);
+ continue;
+ }
+
+ if (!rcBuildRegions(m_rcContext, *tile.chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea))
+ {
+ printf("%s Failed building regions! \n", tileString);
+ continue;
+ }
+
+ tile.cset = rcAllocContourSet();
+ if (!tile.cset || !rcBuildContours(m_rcContext, *tile.chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *tile.cset))
+ {
+ printf("%s Failed building contours! \n", tileString);
+ continue;
+ }
+
+ // build polymesh
+ tile.pmesh = rcAllocPolyMesh();
+ if (!tile.pmesh || !rcBuildPolyMesh(m_rcContext, *tile.cset, tileCfg.maxVertsPerPoly, *tile.pmesh))
+ {
+ printf("%s Failed building polymesh! \n", tileString);
+ continue;
+ }
+
+ tile.dmesh = rcAllocPolyMeshDetail();
+ if (!tile.dmesh || !rcBuildPolyMeshDetail(m_rcContext, *tile.pmesh, *tile.chf, tileCfg.detailSampleDist, tileCfg .detailSampleMaxError, *tile.dmesh))
+ {
+ printf("%s Failed building polymesh detail! \n", tileString);
+ continue;
+ }
+
+ // free those up
+ // we may want to keep them in the future for debug
+ // but right now, we don't have the code to merge them
+ rcFreeHeightField(tile.solid);
+ tile.solid = NULL;
+ rcFreeCompactHeightfield(tile.chf);
+ tile.chf = NULL;
+ rcFreeContourSet(tile.cset);
+ tile.cset = NULL;
+
+ if (tile.pmesh)
+ {
+ pmmerge[nmerge] = tile.pmesh;
+ dmmerge[nmerge] = tile.dmesh;
+ nmerge++;
+ }
+ }
+ }
+
+ iv.polyMesh = rcAllocPolyMesh();
+ if (!iv.polyMesh)
+ {
+ printf("%s alloc iv.polyMesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshes(m_rcContext, pmmerge, nmerge, *iv.polyMesh);
+
+ iv.polyMeshDetail = rcAllocPolyMeshDetail();
+ if (!iv.polyMeshDetail)
+ {
+ printf("%s alloc m_dmesh FIALED!\n", tileString);
+ return;
+ }
+ rcMergePolyMeshDetails(m_rcContext, dmmerge, nmerge, *iv.polyMeshDetail);
+
+ // free things up
+ delete[] pmmerge;
+ delete[] dmmerge;
+
+ delete[] tiles;
+
+ // remove padding for extraction
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] -= (unsigned short)config.borderSize;
+ v[2] -= (unsigned short)config.borderSize;
+ }
+
+ // set polygons as walkable
+ // TODO: special flags for DYNAMIC polygons, ie surfaces that can be turned on and off
+ for (int i = 0; i < iv.polyMesh->npolys; ++i)
+ if (iv.polyMesh->areas[i] & RC_WALKABLE_AREA)
+ iv.polyMesh->flags[i] = iv.polyMesh->areas[i];
+
+ // setup mesh parameters
+ dtNavMeshCreateParams params;
+ memset(&params, 0, sizeof(params));
+ params.verts = iv.polyMesh->verts;
+ params.vertCount = iv.polyMesh->nverts;
+ params.polys = iv.polyMesh->polys;
+ params.polyAreas = iv.polyMesh->areas;
+ params.polyFlags = iv.polyMesh->flags;
+ params.polyCount = iv.polyMesh->npolys;
+ params.nvp = iv.polyMesh->nvp;
+ params.detailMeshes = iv.polyMeshDetail->meshes;
+ params.detailVerts = iv.polyMeshDetail->verts;
+ params.detailVertsCount = iv.polyMeshDetail->nverts;
+ params.detailTris = iv.polyMeshDetail->tris;
+ params.detailTriCount = iv.polyMeshDetail->ntris;
+
+ params.offMeshConVerts = meshData.offMeshConnections.getCArray();
+ params.offMeshConCount = meshData.offMeshConnections.size()/6;
+ params.offMeshConRad = meshData.offMeshConnectionRads.getCArray();
+ params.offMeshConDir = meshData.offMeshConnectionDirs.getCArray();
+ params.offMeshConAreas = meshData.offMeshConnectionsAreas.getCArray();
+ params.offMeshConFlags = meshData.offMeshConnectionsFlags.getCArray();
+
+ params.walkableHeight = BASE_UNIT_DIM*config.walkableHeight; // agent height
+ params.walkableRadius = BASE_UNIT_DIM*config.walkableRadius; // agent radius
+ params.walkableClimb = BASE_UNIT_DIM*config.walkableClimb; // keep less that walkableHeight (aka agent height)!
+ params.tileX = (((bmin[0] + bmax[0]) / 2) - navMesh->getParams()->orig[0]) / GRID_SIZE;
+ params.tileY = (((bmin[2] + bmax[2]) / 2) - navMesh->getParams()->orig[2]) / GRID_SIZE;
+ rcVcopy(params.bmin, bmin);
+ rcVcopy(params.bmax, bmax);
+ params.cs = config.cs;
+ params.ch = config.ch;
+ params.tileSize = VERTEX_PER_MAP;
+
+ // will hold final navmesh
+ unsigned char* navData = NULL;
+ int navDataSize = 0;
+
+ do
+ {
+ // these values are checked within dtCreateNavMeshData - handle them here
+ // so we have a clear error message
+ if (params.nvp > DT_VERTS_PER_POLYGON)
+ {
+ printf("%s Invalid verts-per-polygon value! \n", tileString);
+ continue;
+ }
+ if (params.vertCount >= 0xffff)
+ {
+ printf("%s Too many vertices! \n", tileString);
+ continue;
+ }
+ if (!params.vertCount || !params.verts)
+ {
+ // occurs mostly when adjacent tiles have models
+ // loaded but those models don't span into this tile
+
+ // message is an annoyance
+ //printf("%sNo vertices to build tile! \n", tileString);
+ continue;
+ }
+ if (!params.polyCount || !params.polys ||
+ TILES_PER_MAP*TILES_PER_MAP == params.polyCount)
+ {
+ // we have flat tiles with no actual geometry - don't build those, its useless
+ // keep in mind that we do output those into debug info
+ // drop tiles with only exact count - some tiles may have geometry while having less tiles
+ printf("%s No polygons to build on tile! \n", tileString);
+ continue;
+ }
+ if (!params.detailMeshes || !params.detailVerts || !params.detailTris)
+ {
+ printf("%s No detail mesh to build tile! \n", tileString);
+ continue;
+ }
+
+ printf("%s Building navmesh tile...\n", tileString);
+ if (!dtCreateNavMeshData(&params, &navData, &navDataSize))
+ {
+ printf("%s Failed building navmesh tile! \n", tileString);
+ continue;
+ }
+
+ dtTileRef tileRef = 0;
+ printf("%s Adding tile to navmesh...\n", tileString);
+ // DT_TILE_FREE_DATA tells detour to unallocate memory when the tile
+ // is removed via removeTile()
+ dtStatus dtResult = navMesh->addTile(navData, navDataSize, DT_TILE_FREE_DATA, 0, &tileRef);
+ if (!tileRef || dtResult != DT_SUCCESS)
+ {
+ printf("%s Failed adding tile to navmesh! \n", tileString);
+ continue;
+ }
+
+ // file output
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "wb");
+ if (!file)
+ {
+ char message[1024];
+ sprintf(message, "[Map %i] Failed to open %s for writing!\n", mapID, fileName);
+ perror(message);
+ navMesh->removeTile(tileRef, NULL, NULL);
+ continue;
+ }
+
+ printf("%s Writing to file...\n", tileString);
+
+ // write header
+ MmapTileHeader header;
+ header.usesLiquids = m_terrainBuilder->usesLiquids();
+ header.size = uint32(navDataSize);
+ fwrite(&header, sizeof(MmapTileHeader), 1, file);
+
+ // write data
+ fwrite(navData, sizeof(unsigned char), navDataSize, file);
+ fclose(file);
+
+ // now that tile is written to disk, we can unload it
+ navMesh->removeTile(tileRef, NULL, NULL);
+ }
+ while (0);
+
+ if (m_debugOutput)
+ {
+ // restore padding so that the debug visualization is correct
+ for (int i = 0; i < iv.polyMesh->nverts; ++i)
+ {
+ unsigned short* v = &iv.polyMesh->verts[i*3];
+ v[0] += (unsigned short)config.borderSize;
+ v[2] += (unsigned short)config.borderSize;
+ }
+
+ iv.generateObjFile(mapID, tileX, tileY, meshData);
+ iv.writeIV(mapID, tileX, tileY);
+ }
+ }
+
+ /**************************************************************************/
+ void MapBuilder::getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax)
+ {
+ // this is for elevation
+ if (verts && vertCount)
+ rcCalcBounds(verts, vertCount, bmin, bmax);
+ else
+ {
+ bmin[1] = FLT_MIN;
+ bmax[1] = FLT_MAX;
+ }
+
+ // this is for width and depth
+ bmax[0] = (32 - int(tileX)) * GRID_SIZE;
+ bmax[2] = (32 - int(tileY)) * GRID_SIZE;
+ bmin[0] = bmax[0] - GRID_SIZE;
+ bmin[2] = bmax[2] - GRID_SIZE;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipMap(uint32 mapID)
+ {
+ if (m_skipContinents)
+ switch (mapID)
+ {
+ case 0:
+ case 1:
+ case 530:
+ case 571:
+ return true;
+ default:
+ break;
+ }
+
+ if (m_skipJunkMaps)
+ switch (mapID)
+ {
+ case 13: // test.wdt
+ case 25: // ScottTest.wdt
+ case 29: // Test.wdt
+ case 42: // Colin.wdt
+ case 169: // EmeraldDream.wdt (unused, and very large)
+ case 451: // development.wdt
+ case 573: // ExteriorTest.wdt
+ case 597: // CraigTest.wdt
+ case 605: // development_nonweighted.wdt
+ case 606: // QA_DVD.wdt
+ return true;
+ default:
+ if (isTransportMap(mapID))
+ return true;
+ break;
+ }
+
+ if (m_skipBattlegrounds)
+ switch (mapID)
+ {
+ case 30: // AV
+ case 37: // ?
+ case 489: // WSG
+ case 529: // AB
+ case 566: // EotS
+ case 607: // SotA
+ case 628: // IoC
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::isTransportMap(uint32 mapID)
+ {
+ switch (mapID)
+ {
+ // transport maps
+ case 582:
+ case 584:
+ case 586:
+ case 587:
+ case 588:
+ case 589:
+ case 590:
+ case 591:
+ case 592:
+ case 593:
+ case 594:
+ case 596:
+ case 610:
+ case 612:
+ case 613:
+ case 614:
+ case 620:
+ case 621:
+ case 622:
+ case 623:
+ case 641:
+ case 642:
+ case 647:
+ case 672:
+ case 673:
+ case 712:
+ case 713:
+ case 718:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**************************************************************************/
+ bool MapBuilder::shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY)
+ {
+ char fileName[255];
+ sprintf(fileName, "mmaps/%03u%02i%02i.mmtile", mapID, tileY, tileX);
+ FILE* file = fopen(fileName, "rb");
+ if (!file)
+ return false;
+
+ MmapTileHeader header;
+ fread(&header, sizeof(MmapTileHeader), 1, file);
+ fclose(file);
+
+ if (header.mmapMagic != MMAP_MAGIC || header.dtVersion != DT_NAVMESH_VERSION)
+ return false;
+
+ if (header.mmapVersion != MMAP_VERSION)
+ return false;
+
+ return true;
+ }
+
+}
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
new file mode 100644
index 00000000000..61a71ff6d5b
--- /dev/null
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#ifndef _MAP_BUILDER_H
+#define _MAP_BUILDER_H
+
+#include <vector>
+#include <set>
+#include <map>
+
+#include "TerrainBuilder.h"
+#include "IntermediateValues.h"
+
+#include "IVMapManager.h"
+#include "WorldModel.h"
+
+#include "Recast.h"
+#include "DetourNavMesh.h"
+
+#include "ace/Task.h"
+
+using namespace std;
+using namespace VMAP;
+
+// G3D namespace typedefs conflicts with ACE typedefs
+
+namespace MMAP
+{
+ typedef map<uint32,set<uint32>*> TileList;
+ struct Tile
+ {
+ Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}
+ ~Tile()
+ {
+ rcFreeCompactHeightfield(chf);
+ rcFreeContourSet(cset);
+ rcFreeHeightField(solid);
+ rcFreePolyMesh(pmesh);
+ rcFreePolyMeshDetail(dmesh);
+ }
+ rcCompactHeightfield* chf;
+ rcHeightfield* solid;
+ rcContourSet* cset;
+ rcPolyMesh* pmesh;
+ rcPolyMeshDetail* dmesh;
+ };
+
+ class MapBuilder
+ {
+ public:
+ MapBuilder(float maxWalkableAngle = 60.f,
+ bool skipLiquid = false,
+ bool skipContinents = false,
+ bool skipJunkMaps = true,
+ bool skipBattlegrounds = false,
+ bool debugOutput = false,
+ bool bigBaseUnit = false,
+ const char* offMeshFilePath = NULL);
+
+ ~MapBuilder();
+
+ // builds all mmap tiles for the specified map id (ignores skip settings)
+ void buildMap(uint32 mapID);
+ void buildMeshFromFile(char* name);
+
+ // builds an mmap tile for the specified map and its mesh
+ void buildSingleTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ // builds list of maps, then builds all of mmap tiles (based on the skip settings)
+ void buildAllMaps(int threads);
+
+ private:
+ // detect maps and tiles
+ void discoverTiles();
+ set<uint32>* getTileList(uint32 mapID);
+
+ void buildNavMesh(uint32 mapID, dtNavMesh* &navMesh);
+
+ void buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh);
+
+ // move map building
+ void buildMoveMapTile(uint32 mapID,
+ uint32 tileX,
+ uint32 tileY,
+ MeshData &meshData,
+ float bmin[3],
+ float bmax[3],
+ dtNavMesh* navMesh);
+
+ void getTileBounds(uint32 tileX, uint32 tileY,
+ float* verts, int vertCount,
+ float* bmin, float* bmax);
+ void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY);
+
+ bool shouldSkipMap(uint32 mapID);
+ bool isTransportMap(uint32 mapID);
+ bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY);
+
+ TerrainBuilder* m_terrainBuilder;
+ TileList m_tiles;
+
+ bool m_debugOutput;
+
+ const char* m_offMeshFilePath;
+ bool m_skipContinents;
+ bool m_skipJunkMaps;
+ bool m_skipBattlegrounds;
+
+ float m_maxWalkableAngle;
+ bool m_bigBaseUnit;
+
+ // build performance - not really used for now
+ rcContext* m_rcContext;
+ };
+
+ class BuilderThread : public ACE_Task<ACE_MT_SYNCH>
+ {
+ private:
+ MapBuilder* _builder;
+ uint32 _mapId;
+ public:
+ BuilderThread(MapBuilder* builder) : _builder(builder), Free(true) {}
+
+ void SetMapId(uint32 mapId) { _mapId = mapId; }
+
+ int svc()
+ {
+ Free = false;
+ if (_builder)
+ _builder->buildMap(_mapId);
+ Free = true;
+ return 0;
+ }
+
+ bool Free;
+ };
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
new file mode 100644
index 00000000000..fd02ec02d50
--- /dev/null
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#ifndef _MMAP_COMMON_H
+#define _MMAP_COMMON_H
+
+#include <string>
+#include <vector>
+
+#include "Define.h"
+
+#ifndef _WIN32
+ #include <stddef.h>
+ #include <dirent.h>
+#endif
+
+#ifdef __linux__
+ #include <errno.h>
+#endif
+
+using namespace std;
+
+namespace MMAP
+{
+ inline bool matchWildcardFilter(const char* filter, const char* str)
+ {
+ if (!filter || !str)
+ return false;
+
+ // end on null character
+ while (*filter && *str)
+ {
+ if (*filter == '*')
+ {
+ if (*++filter == '\0') // wildcard at end of filter means all remaing chars match
+ return true;
+
+ while (true)
+ {
+ if (*filter == *str)
+ break;
+ if (*str == '\0')
+ return false; // reached end of string without matching next filter character
+ str++;
+ }
+ }
+ else if (*filter != *str)
+ return false; // mismatch
+
+ filter++;
+ str++;
+ }
+
+ return ((*filter == '\0' || (*filter == '*' && *++filter == '\0')) && *str == '\0');
+ }
+
+ enum ListFilesResult
+ {
+ LISTFILE_DIRECTORY_NOT_FOUND = 0,
+ LISTFILE_OK = 1
+ };
+
+ inline ListFilesResult getDirContents(vector<string> &fileList, string dirpath = ".", string filter = "*", bool includeSubDirs = false)
+ {
+ #ifdef WIN32
+ HANDLE hFind;
+ WIN32_FIND_DATA findFileInfo;
+ string directory;
+
+ directory = dirpath + "/" + filter;
+
+ hFind = FindFirstFile(directory.c_str(), &findFileInfo);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ do
+ {
+ if (includeSubDirs || (findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ fileList.push_back(string(findFileInfo.cFileName));
+ }
+ while (FindNextFile(hFind, &findFileInfo));
+
+ FindClose(hFind);
+
+ #else
+ const char *p = dirpath.c_str();
+ DIR * dirp = opendir(p);
+ struct dirent * dp;
+ dirp = opendir(p);
+
+ while (dirp)
+ {
+ errno = 0;
+ if ((dp = readdir(dirp)) != NULL)
+ {
+ if (matchWildcardFilter(filter.c_str(), dp->d_name))
+ fileList.push_back(string(dp->d_name));
+ }
+ else
+ break;
+ }
+
+ if (dirp)
+ closedir(dirp);
+ else
+ return LISTFILE_DIRECTORY_NOT_FOUND;
+ #endif
+
+ return LISTFILE_OK;
+ }
+}
+
+#endif
diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp
new file mode 100644
index 00000000000..026bf957ec4
--- /dev/null
+++ b/src/tools/mmaps_generator/PathGenerator.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+#include "Timer.h"
+
+using namespace MMAP;
+
+bool checkDirectories(bool debugOutput)
+{
+ vector<string> dirFiles;
+
+ if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size())
+ {
+ printf("'maps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || !dirFiles.size())
+ {
+ printf("'vmaps' directory is empty or does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (getDirContents(dirFiles, "mmaps") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'mmaps' directory does not exist\n");
+ return false;
+ }
+
+ dirFiles.clear();
+ if (debugOutput)
+ {
+ if (getDirContents(dirFiles, "meshes") == LISTFILE_DIRECTORY_NOT_FOUND)
+ {
+ printf("'meshes' directory does not exist (no place to put debugOutput files)\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool handleArgs(int argc, char** argv,
+ int &mapnum,
+ int &tileX,
+ int &tileY,
+ float &maxAngle,
+ bool &skipLiquid,
+ bool &skipContinents,
+ bool &skipJunkMaps,
+ bool &skipBattlegrounds,
+ bool &debugOutput,
+ bool &silent,
+ bool &bigBaseUnit,
+ char* &offMeshInputPath,
+ char* &file,
+ int& threads)
+{
+ char* param = NULL;
+ for (int i = 1; i < argc; ++i)
+ {
+ if (strcmp(argv[i], "--maxAngle") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ float maxangle = atof(param);
+ if (maxangle <= 90.f && maxangle >= 45.f)
+ maxAngle = maxangle;
+ else
+ printf("invalid option for '--maxAngle', using default\n");
+ }
+ else if (strcmp(argv[i], "--threads") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ threads = atoi(param);
+ printf("Using %i threads to extract mmaps\n", threads);
+ }
+ else if (strcmp(argv[i], "--file") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+ file = param;
+ }
+ else if (strcmp(argv[i], "--tile") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ char* stileX = strtok(param, ",");
+ char* stileY = strtok(NULL, ",");
+ int tilex = atoi(stileX);
+ int tiley = atoi(stileY);
+
+ if ((tilex > 0 && tilex < 64) || (tilex == 0 && strcmp(stileX, "0") == 0))
+ tileX = tilex;
+ if ((tiley > 0 && tiley < 64) || (tiley == 0 && strcmp(stileY, "0") == 0))
+ tileY = tiley;
+
+ if (tileX < 0 || tileY < 0)
+ {
+ printf("invalid tile coords.\n");
+ return false;
+ }
+ }
+ else if (strcmp(argv[i], "--skipLiquid") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipLiquid = true;
+ else if (strcmp(param, "false") == 0)
+ skipLiquid = false;
+ else
+ printf("invalid option for '--skipLiquid', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipContinents") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipContinents = true;
+ else if (strcmp(param, "false") == 0)
+ skipContinents = false;
+ else
+ printf("invalid option for '--skipContinents', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipJunkMaps") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipJunkMaps = true;
+ else if (strcmp(param, "false") == 0)
+ skipJunkMaps = false;
+ else
+ printf("invalid option for '--skipJunkMaps', using default\n");
+ }
+ else if (strcmp(argv[i], "--skipBattlegrounds") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ skipBattlegrounds = true;
+ else if (strcmp(param, "false") == 0)
+ skipBattlegrounds = false;
+ else
+ printf("invalid option for '--skipBattlegrounds', using default\n");
+ }
+ else if (strcmp(argv[i], "--debugOutput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ debugOutput = true;
+ else if (strcmp(param, "false") == 0)
+ debugOutput = false;
+ else
+ printf("invalid option for '--debugOutput', using default true\n");
+ }
+ else if (strcmp(argv[i], "--silent") == 0)
+ {
+ silent = true;
+ }
+ else if (strcmp(argv[i], "--bigBaseUnit") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ if (strcmp(param, "true") == 0)
+ bigBaseUnit = true;
+ else if (strcmp(param, "false") == 0)
+ bigBaseUnit = false;
+ else
+ printf("invalid option for '--bigBaseUnit', using default false\n");
+ }
+ else if (strcmp(argv[i], "--offMeshInput") == 0)
+ {
+ param = argv[++i];
+ if (!param)
+ return false;
+
+ offMeshInputPath = param;
+ }
+ else
+ {
+ int map = atoi(argv[i]);
+ if (map > 0 || (map == 0 && (strcmp(argv[i], "0") == 0)))
+ mapnum = map;
+ else
+ {
+ printf("invalid map id\n");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+int finish(const char* message, int returnValue)
+{
+ printf("%s", message);
+ getchar();
+ return returnValue;
+}
+
+int main(int argc, char** argv)
+{
+ int threads = 3, mapnum = -1;
+ float maxAngle = 60.0f;
+ int tileX = -1, tileY = -1;
+ bool skipLiquid = false,
+ skipContinents = false,
+ skipJunkMaps = true,
+ skipBattlegrounds = false,
+ debugOutput = false,
+ silent = false,
+ bigBaseUnit = false;
+ char* offMeshInputPath = NULL;
+ char* file = NULL;
+
+ bool validParam = handleArgs(argc, argv, mapnum,
+ tileX, tileY, maxAngle,
+ skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds,
+ debugOutput, silent, bigBaseUnit, offMeshInputPath, file, threads);
+
+ if (!validParam)
+ return silent ? -1 : finish("You have specified invalid parameters", -1);
+
+ if (mapnum == -1 && debugOutput)
+ {
+ if (silent)
+ return -2;
+
+ printf("You have specifed debug output, but didn't specify a map to generate.\n");
+ printf("This will generate debug output for ALL maps.\n");
+ printf("Are you sure you want to continue? (y/n) ");
+ if (getchar() != 'y')
+ return 0;
+ }
+
+ if (!checkDirectories(debugOutput))
+ return silent ? -3 : finish("Press any key to close...", -3);
+
+ MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps,
+ skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath);
+
+ uint32 start = getMSTime();
+ if (file)
+ builder.buildMeshFromFile(file);
+ else if (tileX > -1 && tileY > -1 && mapnum >= 0)
+ builder.buildSingleTile(mapnum, tileX, tileY);
+ else if (mapnum >= 0)
+ builder.buildMap(uint32(mapnum));
+ else
+ builder.buildAllMaps(threads);
+
+ if (!silent)
+ printf("Finished. MMAPS were built in %u ms!\n", GetMSTimeDiffToNow(start));
+ return 1;
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
new file mode 100644
index 00000000000..7ee4dc7f46c
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.cpp
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#include "TerrainBuilder.h"
+
+#include "PathCommon.h"
+#include "MapBuilder.h"
+
+#include "VMapManager2.h"
+#include "MapTree.h"
+#include "ModelInstance.h"
+
+namespace MMAP
+{
+ TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ }
+ TerrainBuilder::~TerrainBuilder() { }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc)
+ {
+ switch (portion)
+ {
+ case ENTIRE:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ case TOP:
+ loopStart = 0;
+ loopEnd = V8_SIZE;
+ loopInc = 1;
+ break;
+ case LEFT:
+ loopStart = 0;
+ loopEnd = V8_SIZE_SQ - V8_SIZE + 1;
+ loopInc = V8_SIZE;
+ break;
+ case RIGHT:
+ loopStart = V8_SIZE - 1;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = V8_SIZE;
+ break;
+ case BOTTOM:
+ loopStart = V8_SIZE_SQ - V8_SIZE;
+ loopEnd = V8_SIZE_SQ;
+ loopInc = 1;
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ if (loadMap(mapID, tileX, tileY, meshData, ENTIRE))
+ {
+ loadMap(mapID, tileX+1, tileY, meshData, LEFT);
+ loadMap(mapID, tileX-1, tileY, meshData, RIGHT);
+ loadMap(mapID, tileX, tileY+1, meshData, TOP);
+ loadMap(mapID, tileX, tileY-1, meshData, BOTTOM);
+ }
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion)
+ {
+ char mapFileName[255];
+ sprintf(mapFileName, "maps/%03u%02u%02u.map", mapID, tileY, tileX);
+
+ FILE* mapFile = fopen(mapFileName, "rb");
+ if (!mapFile)
+ return false;
+
+ map_fileheader fheader;
+ fread(&fheader, sizeof(map_fileheader), 1, mapFile);
+
+ if (fheader.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)))
+ {
+ fclose(mapFile);
+ printf("%s is the wrong version, please extract new .map files\n", mapFileName);
+ return false;
+ }
+
+ map_heightHeader hheader;
+ fseek(mapFile, fheader.heightMapOffset, SEEK_SET);
+ fread(&hheader, sizeof(map_heightHeader), 1, mapFile);
+
+ bool haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT);
+ bool haveLiquid = fheader.liquidMapOffset && !m_skipLiquid;
+
+ // no data in this map file
+ if (!haveTerrain && !haveLiquid)
+ {
+ fclose(mapFile);
+ return false;
+ }
+
+ // data used later
+ uint16 holes[16][16];
+ memset(holes, 0, sizeof(holes));
+ uint8 liquid_type[16][16];
+ memset(liquid_type, 0, sizeof(liquid_type));
+ G3D::Array<int> ltriangles;
+ G3D::Array<int> ttriangles;
+
+ // terrain data
+ if (haveTerrain)
+ {
+ int i;
+ float heightMultiplier;
+ float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ];
+
+ if (hheader.flags & MAP_HEIGHT_AS_INT8)
+ {
+ uint8 v9[V9_SIZE_SQ];
+ uint8 v8[V8_SIZE_SQ];
+ fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile);
+ fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile);
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255;
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else if (hheader.flags & MAP_HEIGHT_AS_INT16)
+ {
+ uint16 v9[V9_SIZE_SQ];
+ uint16 v8[V8_SIZE_SQ];
+ fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile);
+ fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile);
+ heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535;
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight;
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight;
+ }
+ else
+ {
+ fread (V9, sizeof(float), V9_SIZE_SQ, mapFile);
+ fread(V8, sizeof(float), V8_SIZE_SQ, mapFile);
+ }
+
+ // hole data
+ if (fheader.holesSize != 0)
+ {
+ memset(holes, 0, fheader.holesSize);
+ fseek(mapFile, fheader.holesOffset, SEEK_SET);
+ fread(holes, fheader.holesSize, 1, mapFile);
+ }
+
+ int count = meshData.solidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+
+ for (i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V9, xoffset, yoffset, coord, V9);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ for (i = 0; i < V8_SIZE_SQ; ++i)
+ {
+ getHeightCoord(i, GRID_V8, xoffset, yoffset, coord, V8);
+ meshData.solidVerts.append(coord[0]);
+ meshData.solidVerts.append(coord[2]);
+ meshData.solidVerts.append(coord[1]);
+ }
+
+ int j, indices[3], loopStart, loopEnd, loopInc;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (i = loopStart; i < loopEnd; i+=loopInc)
+ for (j = TOP; j <= BOTTOM; j+=1)
+ {
+ getHeightTriangle(i, Spot(j), indices);
+ ttriangles.append(indices[2] + count);
+ ttriangles.append(indices[1] + count);
+ ttriangles.append(indices[0] + count);
+ }
+ }
+
+ // liquid data
+ if (haveLiquid)
+ {
+ map_liquidHeader lheader;
+ fseek(mapFile, fheader.liquidMapOffset, SEEK_SET);
+ fread(&lheader, sizeof(map_liquidHeader), 1, mapFile);
+
+ float* liquid_map = NULL;
+
+ if (!(lheader.flags & MAP_LIQUID_NO_TYPE))
+ fread(liquid_type, sizeof(liquid_type), 1, mapFile);
+
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ liquid_map = new float [lheader.width*lheader.height];
+ fread(liquid_map, sizeof(float), lheader.width*lheader.height, mapFile);
+ }
+
+ if (liquid_type && liquid_map)
+ {
+ int count = meshData.liquidVerts.size() / 3;
+ float xoffset = (float(tileX)-32)*GRID_SIZE;
+ float yoffset = (float(tileY)-32)*GRID_SIZE;
+
+ float coord[3];
+ int row, col;
+
+ // generate coordinates
+ if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT))
+ {
+ int j = 0;
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+
+ if (row < lheader.offsetY || row >= lheader.offsetY + lheader.height ||
+ col < lheader.offsetX || col >= lheader.offsetX + lheader.width)
+ {
+ // dummy vert using invalid height
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, INVALID_MAP_LIQ_HEIGHT, (yoffset+row*GRID_PART_SIZE)*-1);
+ continue;
+ }
+
+ getLiquidCoord(i, j, xoffset, yoffset, coord, liquid_map);
+ meshData.liquidVerts.append(coord[0]);
+ meshData.liquidVerts.append(coord[2]);
+ meshData.liquidVerts.append(coord[1]);
+ j++;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < V9_SIZE_SQ; ++i)
+ {
+ row = i / V9_SIZE;
+ col = i % V9_SIZE;
+ meshData.liquidVerts.append((xoffset+col*GRID_PART_SIZE)*-1, lheader.liquidLevel, (yoffset+row*GRID_PART_SIZE)*-1);
+ }
+ }
+
+ delete [] liquid_map;
+
+ int indices[3], loopStart, loopEnd, loopInc, triInc;
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ triInc = BOTTOM-TOP;
+
+ // generate triangles
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ for (int j = TOP; j <= BOTTOM; j+= triInc)
+ {
+ getHeightTriangle(i, Spot(j), indices, true);
+ ltriangles.append(indices[2] + count);
+ ltriangles.append(indices[1] + count);
+ ltriangles.append(indices[0] + count);
+ }
+ }
+ }
+
+ fclose(mapFile);
+
+ // now that we have gathered the data, we can figure out which parts to keep:
+ // liquid above ground, ground above liquid
+ int loopStart, loopEnd, loopInc, tTriCount = 4;
+ bool useTerrain, useLiquid;
+
+ float* lverts = meshData.liquidVerts.getCArray();
+ int* ltris = ltriangles.getCArray();
+
+ float* tverts = meshData.solidVerts.getCArray();
+ int* ttris = ttriangles.getCArray();
+
+ if (ltriangles.size() + ttriangles.size() == 0)
+ return false;
+
+ // make a copy of liquid vertices
+ // used to pad right-bottom frame due to lost vertex data at extraction
+ float* lverts_copy = NULL;
+ if (meshData.liquidVerts.size())
+ {
+ lverts_copy = new float[meshData.liquidVerts.size()];
+ memcpy(lverts_copy, lverts, sizeof(float)*meshData.liquidVerts.size());
+ }
+
+ getLoopVars(portion, loopStart, loopEnd, loopInc);
+ for (int i = loopStart; i < loopEnd; i+=loopInc)
+ {
+ for (int j = 0; j < 2; ++j)
+ {
+ // default is true, will change to false if needed
+ useTerrain = true;
+ useLiquid = true;
+ uint8 liquidType = MAP_LIQUID_TYPE_NO_WATER;
+
+ // if there is no liquid, don't use liquid
+ if (!liquid_type || !meshData.liquidVerts.size() || !ltriangles.size())
+ useLiquid = false;
+ else
+ {
+ liquidType = getLiquidType(i, liquid_type);
+ switch (liquidType)
+ {
+ default:
+ useLiquid = false;
+ break;
+ case MAP_LIQUID_TYPE_WATER:
+ case MAP_LIQUID_TYPE_OCEAN:
+ // merge different types of water
+ liquidType = NAV_WATER;
+ break;
+ case MAP_LIQUID_TYPE_MAGMA:
+ liquidType = NAV_MAGMA;
+ break;
+ case MAP_LIQUID_TYPE_SLIME:
+ liquidType = NAV_SLIME;
+ break;
+ case MAP_LIQUID_TYPE_DARK_WATER:
+ // players should not be here, so logically neither should creatures
+ useTerrain = false;
+ useLiquid = false;
+ break;
+ }
+ }
+
+ // if there is no terrain, don't use terrain
+ if (!ttriangles.size())
+ useTerrain = false;
+
+ // while extracting ADT data we are losing right-bottom vertices
+ // this code adds fair approximation of lost data
+ if (useLiquid)
+ {
+ float quadHeight = 0;
+ uint32 validCount = 0;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts_copy[ltris[idx]*3 + 1];
+ if (h != INVALID_MAP_LIQ_HEIGHT && h < INVALID_MAP_LIQ_HEIGHT_MAX)
+ {
+ quadHeight += h;
+ validCount++;
+ }
+ }
+
+ // update vertex height data
+ if (validCount > 0 && validCount < 3)
+ {
+ quadHeight /= validCount;
+ for(uint32 idx = 0; idx < 3; idx++)
+ {
+ float h = lverts[ltris[idx]*3 + 1];
+ if (h == INVALID_MAP_LIQ_HEIGHT || h > INVALID_MAP_LIQ_HEIGHT_MAX)
+ lverts[ltris[idx]*3 + 1] = quadHeight;
+ }
+ }
+
+ // no valid vertexes - don't use this poly at all
+ if (validCount == 0)
+ useLiquid = false;
+ }
+
+ // if there is a hole here, don't use the terrain
+ if (useTerrain && fheader.holesSize != 0)
+ useTerrain = !isHole(i, holes);
+
+ // we use only one terrain kind per quad - pick higher one
+ if (useTerrain && useLiquid)
+ {
+ float minLLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ float maxLLevel = INVALID_MAP_LIQ_HEIGHT;
+ for(uint32 x = 0; x < 3; x++)
+ {
+ float h = lverts[ltris[x]*3 + 1];
+ if (minLLevel > h)
+ minLLevel = h;
+
+ if (maxLLevel < h)
+ maxLLevel = h;
+ }
+
+ float maxTLevel = INVALID_MAP_LIQ_HEIGHT;
+ float minTLevel = INVALID_MAP_LIQ_HEIGHT_MAX;
+ for(uint32 x = 0; x < 6; x++)
+ {
+ float h = tverts[ttris[x]*3 + 1];
+ if (maxTLevel < h)
+ maxTLevel = h;
+
+ if (minTLevel > h)
+ minTLevel = h;
+ }
+
+ // terrain under the liquid?
+ if (minLLevel > maxTLevel)
+ useTerrain = false;
+
+ //liquid under the terrain?
+ if (minTLevel > maxLLevel)
+ useLiquid = false;
+ }
+
+ // store the result
+ if (useLiquid)
+ {
+ meshData.liquidType.append(liquidType);
+ for (int k = 0; k < 3; ++k)
+ meshData.liquidTris.append(ltris[k]);
+ }
+
+ if (useTerrain)
+ for (int k = 0; k < 3*tTriCount/2; ++k)
+ meshData.solidTris.append(ttris[k]);
+
+ // advance to next set of triangles
+ ltris += 3;
+ ttris += 3*tTriCount/2;
+ }
+ }
+
+ if (lverts_copy)
+ delete [] lverts_copy;
+
+ return meshData.solidTris.size() || meshData.liquidTris.size();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ switch (grid)
+ {
+ case GRID_V9:
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index];
+ break;
+ case GRID_V8:
+ coord[0] = (xOffset + index%(V8_SIZE)*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V8_SIZE))*GRID_PART_SIZE + GRID_PART_SIZE/2.f) * -1.f;
+ coord[2] = v[index];
+ break;
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getHeightTriangle(int square, Spot triangle, int* indices, bool liquid/* = false*/)
+ {
+ int rowOffset = square/V8_SIZE;
+ if (!liquid)
+ switch (triangle)
+ {
+ case TOP:
+ indices[0] = square+rowOffset; // 0-----1 .... 128
+ indices[1] = square+1+rowOffset; // |\ T /|
+ indices[2] = (V9_SIZE_SQ)+square; // | \ / |
+ break; // |L 0 R| .. 127
+ case LEFT: // | / \ |
+ indices[0] = square+rowOffset; // |/ B \|
+ indices[1] = (V9_SIZE_SQ)+square; // 129---130 ... 386
+ indices[2] = square+V9_SIZE+rowOffset; // |\ /|
+ break; // | \ / |
+ case RIGHT: // | 128 | .. 255
+ indices[0] = square+1+rowOffset; // | / \ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // |/ \|
+ indices[2] = (V9_SIZE_SQ)+square; // 258---259 ... 515
+ break;
+ case BOTTOM:
+ indices[0] = (V9_SIZE_SQ)+square;
+ indices[1] = square+V9_SIZE+1+rowOffset;
+ indices[2] = square+V9_SIZE+rowOffset;
+ break;
+ default: break;
+ }
+ else
+ switch (triangle)
+ { // 0-----1 .... 128
+ case TOP: // |\ |
+ indices[0] = square+rowOffset; // | \ T |
+ indices[1] = square+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+1+rowOffset; // | B \ |
+ break; // | \|
+ case BOTTOM: // 129---130 ... 386
+ indices[0] = square+rowOffset; // |\ |
+ indices[1] = square+V9_SIZE+1+rowOffset; // | \ |
+ indices[2] = square+V9_SIZE+rowOffset; // | \ |
+ break; // | \ |
+ default: break; // | \|
+ } // 258---259 ... 515
+
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v)
+ {
+ // wow coords: x, y, height
+ // coord is mirroed about the horizontal axes
+ coord[0] = (xOffset + index%(V9_SIZE)*GRID_PART_SIZE) * -1.f;
+ coord[1] = (yOffset + (int)(index/(V9_SIZE))*GRID_PART_SIZE) * -1.f;
+ coord[2] = v[index2];
+ }
+
+ static uint16 holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888};
+ static uint16 holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000};
+
+ /**************************************************************************/
+ bool TerrainBuilder::isHole(int square, const uint16 holes[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+ int holeRow = row % 8 / 2;
+ int holeCol = (square - (row * 128 + cellCol * 8)) / 2;
+
+ uint16 hole = holes[cellRow][cellCol];
+
+ return (hole & holetab_h[holeCol] & holetab_v[holeRow]) != 0;
+ }
+
+ /**************************************************************************/
+ uint8 TerrainBuilder::getLiquidType(int square, const uint8 liquid_type[16][16])
+ {
+ int row = square / 128;
+ int col = square % 128;
+ int cellRow = row / 8; // 8 squares per cell
+ int cellCol = col / 8;
+
+ return liquid_type[cellRow][cellCol];
+ }
+
+ /**************************************************************************/
+ bool TerrainBuilder::loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData)
+ {
+ IVMapManager* vmapManager = new VMapManager2();
+ int result = vmapManager->loadMap("vmaps", mapID, tileX, tileY);
+ bool retval = false;
+
+ do
+ {
+ if (result == VMAP_LOAD_RESULT_ERROR)
+ break;
+
+ InstanceTreeMap instanceTrees;
+ ((VMapManager2*)vmapManager)->getInstanceMapTree(instanceTrees);
+
+ if (!instanceTrees[mapID])
+ break;
+
+ ModelInstance* models = NULL;
+ uint32 count = 0;
+ instanceTrees[mapID]->getModelInstances(models, count);
+
+ if (!models)
+ break;
+
+ for (uint32 i = 0; i < count; ++i)
+ {
+ ModelInstance instance = models[i];
+
+ // model instances exist in tree even though there are instances of that model in this tile
+ WorldModel* worldModel = instance.getWorldModel();
+ if (!worldModel)
+ continue;
+
+ // now we have a model to add to the meshdata
+ retval = true;
+
+ vector<GroupModel> groupModels;
+ worldModel->getGroupModels(groupModels);
+
+ // all M2s need to have triangle indices reversed
+ bool isM2 = instance.name.find(".m2") != instance.name.npos || instance.name.find(".M2") != instance.name.npos;
+
+ // transform data
+ float scale = instance.iScale;
+ G3D::Matrix3 rotation = G3D::Matrix3::fromEulerAnglesXYZ(G3D::pi()*instance.iRot.z/-180.f, G3D::pi()*instance.iRot.x/-180.f, G3D::pi()*instance.iRot.y/-180.f);
+ Vector3 position = instance.iPos;
+ position.x -= 32*GRID_SIZE;
+ position.y -= 32*GRID_SIZE;
+
+ for (vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it)
+ {
+ vector<Vector3> tempVertices;
+ vector<Vector3> transformedVertices;
+ vector<MeshTriangle> tempTriangles;
+ WmoLiquid* liquid = NULL;
+
+ (*it).getMeshData(tempVertices, tempTriangles, liquid);
+
+ // first handle collision mesh
+ transform(tempVertices, transformedVertices, scale, rotation, position);
+
+ int offset = meshData.solidVerts.size() / 3;
+
+ copyVertices(transformedVertices, meshData.solidVerts);
+ copyIndices(tempTriangles, meshData.solidTris, offset, isM2);
+
+ // now handle liquid data
+ if (liquid)
+ {
+ vector<Vector3> liqVerts;
+ vector<int> liqTris;
+ uint32 tilesX, tilesY, vertsX, vertsY;
+ Vector3 corner;
+ liquid->getPosInfo(tilesX, tilesY, corner);
+ vertsX = tilesX + 1;
+ vertsY = tilesY + 1;
+ uint8* flags = liquid->GetFlagsStorage();
+ float* data = liquid->GetHeightStorage();
+ uint8 type = NAV_EMPTY;
+
+ // convert liquid type to NavTerrain
+ switch (liquid->GetType())
+ {
+ case 0:
+ case 1:
+ type = NAV_WATER;
+ break;
+ case 2:
+ type = NAV_MAGMA;
+ break;
+ case 3:
+ type = NAV_SLIME;
+ break;
+ }
+
+ // indexing is weird...
+ // after a lot of trial and error, this is what works:
+ // vertex = y*vertsX+x
+ // tile = x*tilesY+y
+ // flag = y*tilesY+x
+
+ Vector3 vert;
+ for (uint32 x = 0; x < vertsX; ++x)
+ for (uint32 y = 0; y < vertsY; ++y)
+ {
+ vert = Vector3(corner.x + x * GRID_PART_SIZE, corner.y + y * GRID_PART_SIZE, data[y*vertsX + x]);
+ vert = vert * rotation * scale + position;
+ vert.x *= -1.f;
+ vert.y *= -1.f;
+ liqVerts.push_back(vert);
+ }
+
+ int idx1, idx2, idx3, idx4;
+ uint32 square;
+ for (uint32 x = 0; x < tilesX; ++x)
+ for (uint32 y = 0; y < tilesY; ++y)
+ if ((flags[x+y*tilesX] & 0x0f) != 0x0f)
+ {
+ square = x * tilesY + y;
+ idx1 = square+x;
+ idx2 = square+1+x;
+ idx3 = square+tilesY+1+1+x;
+ idx4 = square+tilesY+1+x;
+
+ // top triangle
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx2);
+ liqTris.push_back(idx1);
+ // bottom triangle
+ liqTris.push_back(idx4);
+ liqTris.push_back(idx3);
+ liqTris.push_back(idx1);
+ }
+
+ uint32 liqOffset = meshData.liquidVerts.size() / 3;
+ for (uint32 i = 0; i < liqVerts.size(); ++i)
+ meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
+
+ for (uint32 i = 0; i < liqTris.size() / 3; ++i)
+ {
+ meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset);
+ meshData.liquidType.append(type);
+ }
+ }
+ }
+ }
+ }
+ while (false);
+
+ vmapManager->unloadMap(mapID, tileX, tileY);
+ delete vmapManager;
+
+ return retval;
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::transform(vector<Vector3> &source, vector<Vector3> &transformedVertices, float scale, G3D::Matrix3 &rotation, Vector3 &position)
+ {
+ for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ // apply tranform, then mirror along the horizontal axes
+ Vector3 v((*it) * rotation * scale + position);
+ v.x *= -1.f;
+ v.y *= -1.f;
+ transformedVertices.push_back(v);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyVertices(vector<Vector3> &source, G3D::Array<float> &dest)
+ {
+ for (vector<Vector3>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).y);
+ dest.push_back((*it).z);
+ dest.push_back((*it).x);
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(vector<MeshTriangle> &source, G3D::Array<int> &dest, int offset, bool flip)
+ {
+ if (flip)
+ {
+ for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx2+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx0+offset);
+ }
+ }
+ else
+ {
+ for (vector<MeshTriangle>::iterator it = source.begin(); it != source.end(); ++it)
+ {
+ dest.push_back((*it).idx0+offset);
+ dest.push_back((*it).idx1+offset);
+ dest.push_back((*it).idx2+offset);
+ }
+ }
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::copyIndices(G3D::Array<int> &source, G3D::Array<int> &dest, int offset)
+ {
+ int* src = source.getCArray();
+ for (int32 i = 0; i < source.size(); ++i)
+ dest.append(src[i] + offset);
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris)
+ {
+ map<int, int> vertMap;
+
+ int* t = tris.getCArray();
+ float* v = verts.getCArray();
+
+ G3D::Array<float> cleanVerts;
+ int index, count = 0;
+ // collect all the vertex indices from triangle
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ if (vertMap.find(t[i]) != vertMap.end())
+ continue;
+ std::pair<int, int> val;
+ val.first = t[i];
+
+ index = val.first;
+ val.second = count;
+
+ vertMap.insert(val);
+ cleanVerts.append(v[index * 3], v[index * 3 + 1], v[index * 3 + 2]);
+ count++;
+ }
+
+ verts.fastClear();
+ verts.append(cleanVerts);
+ cleanVerts.clear();
+
+ // update triangles to use new indices
+ for (int i = 0; i < tris.size(); ++i)
+ {
+ map<int, int>::iterator it;
+ if ((it = vertMap.find(t[i])) == vertMap.end())
+ continue;
+
+ t[i] = (*it).second;
+ }
+
+ vertMap.clear();
+ }
+
+ /**************************************************************************/
+ void TerrainBuilder::loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath)
+ {
+ // no meshfile input given?
+ if (offMeshFilePath == NULL)
+ return;
+
+ FILE* fp = fopen(offMeshFilePath, "rb");
+ if (!fp)
+ {
+ printf(" loadOffMeshConnections:: input file %s not found!\n", offMeshFilePath);
+ return;
+ }
+
+ // pretty silly thing, as we parse entire file and load only the tile we need
+ // but we don't expect this file to be too large
+ char* buf = new char[512];
+ while(fgets(buf, 512, fp))
+ {
+ float p0[3], p1[3];
+ int mid, tx, ty;
+ float size;
+ if (10 != sscanf(buf, "%d %d,%d (%f %f %f) (%f %f %f) %f", &mid, &tx, &ty,
+ &p0[0], &p0[1], &p0[2], &p1[0], &p1[1], &p1[2], &size))
+ continue;
+
+ if (mapID == mid, tileX == tx, tileY == ty)
+ {
+ meshData.offMeshConnections.append(p0[1]);
+ meshData.offMeshConnections.append(p0[2]);
+ meshData.offMeshConnections.append(p0[0]);
+
+ meshData.offMeshConnections.append(p1[1]);
+ meshData.offMeshConnections.append(p1[2]);
+ meshData.offMeshConnections.append(p1[0]);
+
+ meshData.offMeshConnectionDirs.append(1); // 1 - both direction, 0 - one sided
+ meshData.offMeshConnectionRads.append(size); // agent size equivalent
+ // can be used same way as polygon flags
+ meshData.offMeshConnectionsAreas.append((unsigned char)0xFF);
+ meshData.offMeshConnectionsFlags.append((unsigned short)0xFF); // all movement masks can make this path
+ }
+
+ }
+
+ delete [] buf;
+ fclose(fp);
+ }
+}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h
new file mode 100644
index 00000000000..6d478753279
--- /dev/null
+++ b/src/tools/mmaps_generator/TerrainBuilder.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#ifndef _MMAP_TERRAIN_BUILDER_H
+#define _MMAP_TERRAIN_BUILDER_H
+
+#include "PathCommon.h"
+#include "Map.h"
+#include "SharedDefines.h"
+
+#include "WorldModel.h"
+
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+
+using namespace Trinity;
+
+namespace MMAP
+{
+ enum Spot
+ {
+ TOP = 1,
+ RIGHT = 2,
+ LEFT = 3,
+ BOTTOM = 4,
+ ENTIRE = 5
+ };
+
+ enum Grid
+ {
+ GRID_V8,
+ GRID_V9
+ };
+
+ static const int V9_SIZE = 129;
+ static const int V9_SIZE_SQ = V9_SIZE*V9_SIZE;
+ static const int V8_SIZE = 128;
+ static const int V8_SIZE_SQ = V8_SIZE*V8_SIZE;
+ static const float GRID_SIZE = 533.33333f;
+ static const float GRID_PART_SIZE = GRID_SIZE/V8_SIZE;
+
+ // see contrib/extractor/system.cpp, CONF_use_minHeight
+ static const float INVALID_MAP_LIQ_HEIGHT = -500.f;
+ static const float INVALID_MAP_LIQ_HEIGHT_MAX = 5000.0f;
+
+ // see following files:
+ // contrib/extractor/system.cpp
+ // src/game/Map.cpp
+ static char const* MAP_VERSION_MAGIC = "v1.3";
+
+ struct MeshData
+ {
+ G3D::Array<float> solidVerts;
+ G3D::Array<int> solidTris;
+
+ G3D::Array<float> liquidVerts;
+ G3D::Array<int> liquidTris;
+ G3D::Array<uint8> liquidType;
+
+ // offmesh connection data
+ G3D::Array<float> offMeshConnections; // [p0y,p0z,p0x,p1y,p1z,p1x] - per connection
+ G3D::Array<float> offMeshConnectionRads;
+ G3D::Array<unsigned char> offMeshConnectionDirs;
+ G3D::Array<unsigned char> offMeshConnectionsAreas;
+ G3D::Array<unsigned short> offMeshConnectionsFlags;
+ };
+
+ class TerrainBuilder
+ {
+ public:
+ TerrainBuilder(bool skipLiquid);
+ ~TerrainBuilder();
+
+ void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
+ void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath);
+
+ bool usesLiquids() { return !m_skipLiquid; }
+
+ // vert and triangle methods
+ static void transform(vector<G3D::Vector3> &original, vector<G3D::Vector3> &transformed,
+ float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position);
+ static void copyVertices(vector<G3D::Vector3> &source, G3D::Array<float> &dest);
+ static void copyIndices(vector<VMAP::MeshTriangle> &source, G3D::Array<int> &dest, int offest, bool flip);
+ static void copyIndices(G3D::Array<int> &src, G3D::Array<int> &dest, int offset);
+ static void cleanVertices(G3D::Array<float> &verts, G3D::Array<int> &tris);
+ private:
+ /// Loads a portion of a map's terrain
+ bool loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, Spot portion);
+
+ /// Sets loop variables for selecting only certain parts of a map's terrain
+ void getLoopVars(Spot portion, int &loopStart, int &loopEnd, int &loopInc);
+
+ /// Controls whether liquids are loaded
+ bool m_skipLiquid;
+
+ /// Load the map terrain from file
+ bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float> &vertices, G3D::Array<int> &triangles, Spot portion);
+
+ /// Get the vector coordinate for a specific position
+ void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the triangle's vector indices for a specific position
+ void getHeightTriangle(int square, Spot triangle, int* indices, bool liquid = false);
+
+ /// Determines if the specific position's triangles should be rendered
+ bool isHole(int square, const uint16 holes[16][16]);
+
+ /// Get the liquid vector coordinate for a specific position
+ void getLiquidCoord(int index, int index2, float xOffset, float yOffset, float* coord, float* v);
+
+ /// Get the liquid type for a specific position
+ uint8 getLiquidType(int square, const uint8 liquid_type[16][16]);
+
+ // hide parameterless and copy constructor
+ TerrainBuilder();
+ TerrainBuilder(const TerrainBuilder &tb);
+ };
+}
+
+#endif
+
diff --git a/src/tools/mmaps_generator/VMapExtensions.cpp b/src/tools/mmaps_generator/VMapExtensions.cpp
new file mode 100644
index 00000000000..4f203e11c21
--- /dev/null
+++ b/src/tools/mmaps_generator/VMapExtensions.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2011 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
+ *
+ * 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/>.
+ */
+
+#include <vector>
+#include "MapTree.h"
+#include "VMapManager2.h"
+#include "WorldModel.h"
+#include "ModelInstance.h"
+
+using namespace std;
+
+namespace VMAP
+{
+ // Need direct access to encapsulated VMAP data, so we add functions for MMAP generator
+ // maybe add MapBuilder as friend to all of the below classes would be better?
+
+ // declared in src/shared/vmap/MapTree.h
+ void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
+ {
+ models = iTreeValues;
+ count = iNTreeValues;
+ }
+
+ // declared in src/shared/vmap/VMapManager2.h
+ void VMapManager2::getInstanceMapTree(InstanceTreeMap &instanceMapTree)
+ {
+ instanceMapTree = iInstanceMapTrees;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WorldModel::getGroupModels(vector<GroupModel> &groupModels)
+ {
+ groupModels = this->groupModels;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void GroupModel::getMeshData(vector<Vector3> &vertices, vector<MeshTriangle> &triangles, WmoLiquid* &liquid)
+ {
+ vertices = this->vertices;
+ triangles = this->triangles;
+ liquid = iLiquid;
+ }
+
+ // declared in src/shared/vmap/ModelInstance.h
+ WorldModel* const ModelInstance::getWorldModel()
+ {
+ return iModel;
+ }
+
+ // declared in src/shared/vmap/WorldModel.h
+ void WmoLiquid::getPosInfo(uint32 &tilesX, uint32 &tilesY, Vector3 &corner) const
+ {
+ tilesX = iTilesX;
+ tilesY = iTilesY;
+ corner = iCorner;
+ }
+}