diff options
Diffstat (limited to 'src')
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(¶ms, sizeof(dtNavMeshParams), 1, file); + fclose(file); + + dtNavMesh* mesh = dtAllocNavMesh(); + ASSERT(mesh); + if (DT_SUCCESS != mesh->init(¶ms)) + { + 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(¶ms, 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(¶ms, &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; + } +} |