diff options
Diffstat (limited to 'src')
221 files changed, 14935 insertions, 3758 deletions
diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 7d85cdb6e21..e8816ea8816 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -30,5 +30,6 @@ if( SERVERS ) else() if( TOOLS ) add_subdirectory(collision) + add_subdirectory(shared) endif() endif() diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index f4f73e2c33d..c87df4ef7ab 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -134,8 +134,6 @@ extern int main(int argc, char **argv) if (!StartDB()) return 1; - sLog->SetRealmID(0); // ensure we've set realm to 0 (authserver realmid) - // Get the list of realms for the server sRealmList->Initialize(ConfigMgr::GetIntDefault("RealmsStateUpdateDelay", 20)); if (sRealmList->size() == 0) @@ -272,7 +270,7 @@ bool StartDB() } sLog->outInfo(LOG_FILTER_AUTHSERVER, "Started auth database connection pool."); - sLog->EnableDBAppenders(); + sLog->SetRealmId(0); // Enables DB appenders when realm is set. return true; } diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 67d22c49da1..dda19c3b849 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -154,7 +154,7 @@ LoginDatabase.WorkerThreads = 1 # Appender config values: Given a appender "name" # Appender.name # Description: Defines 'where to log' -# Format: Type,LogLevel,Flags,optional1,optional2 +# Format: Type,LogLevel,Flags,optional1,optional2,optional3 # # Type # 0 - (None) @@ -205,6 +205,13 @@ LoginDatabase.WorkerThreads = 1 # a - (Append) # w - (Overwrite) # +# MaxFileSize: Maximum file size of the log file before creating a new log file +# (read as optional3 if Type = File) +# Size is measured in bytes expressed in a 64-bit unsigned integer. +# Maximum value is 4294967295 (4 gb). Leave blank for no limit. +# NOTE: Does not work with dynamic filenames. +# Example: 536870912 (512 mb) +# Appender.Console=1,2,0 Appender.Auth=2,2,0,Auth.log,w @@ -250,4 +257,4 @@ Logger.Root=0,3,Console Auth Loggers=Root # -###################################################################################################
\ No newline at end of file +################################################################################################### diff --git a/src/server/collision/BoundingIntervalHierarchyWrapper.h b/src/server/collision/BoundingIntervalHierarchyWrapper.h index 8a99078caab..315f3004306 100644 --- a/src/server/collision/BoundingIntervalHierarchyWrapper.h +++ b/src/server/collision/BoundingIntervalHierarchyWrapper.h @@ -33,11 +33,14 @@ class BIHWrap { const T* const* objects; RayCallback& _callback; + uint32 objects_size; - MDLCallback(RayCallback& callback, const T* const* objects_array ) : objects(objects_array), _callback(callback) {} + MDLCallback(RayCallback& callback, const T* const* objects_array, uint32 objects_size ) : objects(objects_array), _callback(callback), objects_size(objects_size) {} bool operator() (const Ray& ray, uint32 Idx, float& MaxDist, bool /*stopAtFirst*/) { + if (Idx >= objects_size) + return false; if (const T* obj = objects[Idx]) return _callback(ray, *obj, MaxDist/*, stopAtFirst*/); return false; @@ -45,6 +48,8 @@ class BIHWrap void operator() (const Vector3& p, uint32 Idx) { + if (Idx >= objects_size) + return false; if (const T* obj = objects[Idx]) _callback(p, *obj); } @@ -87,21 +92,24 @@ public: m_objects.fastClear(); m_obj2Idx.getKeys(m_objects); m_objects_to_push.getMembers(m_objects); + //assert that m_obj2Idx has all the keys m_tree.build(m_objects, BoundsFunc::getBounds2); } template<typename RayCallback> - void intersectRay(const Ray& ray, RayCallback& intersectCallback, float& maxDist) const + void intersectRay(const Ray& ray, RayCallback& intersectCallback, float& maxDist) { - MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray()); + balance(); + MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray(), m_objects.size()); m_tree.intersectRay(ray, temp_cb, maxDist, true); } template<typename IsectCallback> - void intersectPoint(const Vector3& point, IsectCallback& intersectCallback) const + void intersectPoint(const Vector3& point, IsectCallback& intersectCallback) { - MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray()); + balance(); + MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray(), m_objects.size()); m_tree.intersectPoint(point, callback); } }; diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt index 62af7dd081e..b4012b63812 100644 --- a/src/server/collision/CMakeLists.txt +++ b/src/server/collision/CMakeLists.txt @@ -33,7 +33,9 @@ set(collision_STAT_SRCS include_directories( ${CMAKE_BINARY_DIR} ${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..7adf7fbfa66 --- /dev/null +++ b/src/server/collision/Management/MMapFactory.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008-2013 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 "DisableMgr.h" + +namespace MMAP +{ + // ######################## MMapFactory ######################## + // our global singleton copy + MMapManager *g_MMapManager = NULL; + + MMapManager* MMapFactory::createOrGetMMapManager() + { + if (g_MMapManager == NULL) + g_MMapManager = new MMapManager(); + + return g_MMapManager; + } + + bool MMapFactory::IsPathfindingEnabled(uint32 mapId) + { + return sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) + && !DisableMgr::IsDisabledFor(DISABLE_TYPE_MMAP, mapId, NULL, MMAP_DISABLE_PATHFINDING); + } + + void MMapFactory::clear() + { + 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..00f19a194d3 --- /dev/null +++ b/src/server/collision/Management/MMapFactory.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008-2013 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 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..e3ed8f3310a --- /dev/null +++ b/src/server/collision/Management/MMapManager.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2008-2013 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; + int count = fread(¶ms, sizeof(dtNavMeshParams), 1, file); + fclose(file); + if (count != 1) + { + sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName); + delete [] fileName; + return false; + } + + 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]; + + snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y); + + 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; + if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC) + { + sLog->outError(LOG_FILTER_MAPS, "MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y); + 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, x, y); + 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, x, y, 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, x, y); + 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, x, y); + 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, x, y); + 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, x, y); + ASSERT(false); + } + else + { + mmap->mmapLoadedTiles.erase(packedGridPos); + --loadedTiles; + sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, 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, x, y); + else + { + --loadedTiles; + sLog->outInfo(LOG_FILTER_MAPS, "MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, 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]; + } +} diff --git a/src/server/collision/Management/MMapManager.h b/src/server/collision/Management/MMapManager.h new file mode 100644 index 00000000000..56b1b856d65 --- /dev/null +++ b/src/server/collision/Management/MMapManager.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008-2013 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.cpp b/src/server/collision/Management/VMapManager2.cpp index 5e3741ca753..8a1bd346957 100644 --- a/src/server/collision/Management/VMapManager2.cpp +++ b/src/server/collision/Management/VMapManager2.cpp @@ -24,13 +24,13 @@ #include "MapTree.h" #include "ModelInstance.h" #include "WorldModel.h" -#include "VMapDefinitions.h" -#include "Log.h" #include <G3D/Vector3.h> #include <ace/Null_Mutex.h> #include <ace/Singleton.h> #include "DisableMgr.h" #include "DBCStores.h" +#include "Log.h" +#include "VMapDefinitions.h" using G3D::Vector3; @@ -257,11 +257,11 @@ namespace VMAP WorldModel* worldmodel = new WorldModel(); if (!worldmodel->readFile(basepath + filename + ".vmo")) { - sLog->outError(LOG_FILTER_GENERAL, "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str()); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str()); delete worldmodel; return NULL; } - sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str()); + VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str()); model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first; model->second.setModel(worldmodel); } @@ -277,12 +277,12 @@ namespace VMAP ModelFileMap::iterator model = iLoadedModelFiles.find(filename); if (model == iLoadedModelFiles.end()) { - sLog->outError(LOG_FILTER_GENERAL, "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str()); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "VMapManager2: trying to unload non-loaded file '%s'", filename.c_str()); return; } if (model->second.decRefCount() == 0) { - sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str()); + VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str()); delete model->second.getModel(); iLoadedModelFiles.erase(model); } diff --git a/src/server/collision/Management/VMapManager2.h b/src/server/collision/Management/VMapManager2.h index e2e9965dbfc..51f15f0fda4 100644 --- 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.cpp b/src/server/collision/Maps/MapTree.cpp index 5f4e2b6aa8b..eb4b4555cb4 100644 --- a/src/server/collision/Maps/MapTree.cpp +++ b/src/server/collision/Maps/MapTree.cpp @@ -21,18 +21,13 @@ #include "VMapManager2.h" #include "VMapDefinitions.h" #include "Log.h" +#include "Errors.h" #include <string> #include <sstream> #include <iomanip> #include <limits> -#ifndef NO_CORE_FUNCS - #include "Errors.h" -#else - #define ASSERT(x) -#endif - using G3D::Vector3; namespace VMAP @@ -277,7 +272,7 @@ namespace VMAP bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm) { - sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str()); + VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str()); bool success = true; std::string fullname = iBasePath + fname; FILE* rf = fopen(fullname.c_str(), "rb"); @@ -310,7 +305,7 @@ namespace VMAP if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn)) { WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name); - sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str()); + VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str()); if (model) { // assume that global model always is the first and only tree value (could be improved...) @@ -320,7 +315,7 @@ namespace VMAP else { success = false; - sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str()); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str()); } } @@ -356,7 +351,7 @@ namespace VMAP } if (!iTreeValues) { - sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY); return false; } bool result = true; @@ -382,7 +377,7 @@ namespace VMAP // acquire model instance WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name); if (!model) - sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY); // update tree uint32 referencedVal; @@ -432,7 +427,7 @@ namespace VMAP loadedTileMap::iterator tile = iLoadedTiles.find(tileID); if (tile == iLoadedTiles.end()) { - sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY); return; } if (tile->second) // file associated with tile @@ -466,7 +461,7 @@ namespace VMAP else { if (!iLoadedSpawns.count(referencedNode)) - sLog->outError(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID); else if (--iLoadedSpawns[referencedNode] == 0) { iTreeValues[referencedNode].setUnloaded(); @@ -480,5 +475,4 @@ namespace VMAP } iLoadedTiles.erase(tile); } - } diff --git a/src/server/collision/Maps/MapTree.h b/src/server/collision/Maps/MapTree.h index bd928d85c5a..c8e9628dff1 100644 --- 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/GameObjectModel.cpp b/src/server/collision/Models/GameObjectModel.cpp index 0ecf02648f9..54283389387 100644 --- a/src/server/collision/Models/GameObjectModel.cpp +++ b/src/server/collision/Models/GameObjectModel.cpp @@ -34,8 +34,6 @@ using G3D::Vector3; using G3D::Ray; using G3D::AABox; -#ifndef NO_CORE_FUNCS - struct GameobjectModelData { GameobjectModelData(const std::string& name_, const AABox& box) : @@ -54,7 +52,7 @@ void LoadGameObjectModelList() FILE* model_list_file = fopen((sWorld->GetDataPath() + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb"); if (!model_list_file) { - sLog->outError(LOG_FILTER_GENERAL, "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS); return; } @@ -73,7 +71,7 @@ void LoadGameObjectModelList() || fread(&v1, sizeof(Vector3), 1, model_list_file) != 1 || fread(&v2, sizeof(Vector3), 1, model_list_file) != 1) { - sLog->outError(LOG_FILTER_GENERAL, "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS); break; } @@ -84,8 +82,7 @@ void LoadGameObjectModelList() } fclose(model_list_file); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime)); - + VMAP_INFO_LOG(LOG_FILTER_SERVER_LOADING, ">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime)); } GameObjectModel::~GameObjectModel() @@ -104,7 +101,7 @@ bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayIn // ignore models with no bounds if (mdl_box == G3D::AABox::zero()) { - sLog->outError(LOG_FILTER_GENERAL, "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str()); + VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str()); return false; } @@ -184,5 +181,3 @@ bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool Sto } return hit; } - -#endif diff --git a/src/server/collision/Models/ModelInstance.h b/src/server/collision/Models/ModelInstance.h index 5af02a1d1b1..f26089bb46c 100644 --- 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* getWorldModel(); }; } // namespace VMAP diff --git a/src/server/collision/Models/WorldModel.h b/src/server/collision/Models/WorldModel.h index 8e046e561f7..ade9efbb040 100644 --- 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/collision/VMapDefinitions.h b/src/server/collision/VMapDefinitions.h index 609d00cd00f..bb0766dc8ff 100644 --- a/src/server/collision/VMapDefinitions.h +++ b/src/server/collision/VMapDefinitions.h @@ -31,4 +31,16 @@ namespace VMAP // defined in TileAssembler.cpp currently... bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len); } + +// Set of helper macros for extractors (VMAP and MMAP) +#ifndef NO_CORE_FUNCS +#define VMAP_ERROR_LOG(FILTER, ...) sLog->outError(FILTER, __VA_ARGS__) +#define VMAP_DEBUG_LOG(FILTER, ...) sLog->outDebug(FILTER, __VA_ARGS__) +#define VMAP_INFO_LOG(FILTER, ...) sLog->outInfo(FILTER, __VA_ARGS__) +#else +#define VMAP_ERROR_LOG(FILTER, ...) (void)sizeof(FILTER) +#define VMAP_DEBUG_LOG(FILTER, ...) (void)sizeof(FILTER) +#define VMAP_INFO_LOG(FILTER, ...) (void)sizeof(FILTER) +#endif + #endif diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index c08e085e816..86d86739bc9 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2011,6 +2011,17 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u } } +void SmartScript::ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellInfo* spell, GameObject* gob) +{ + ConditionList const conds = sConditionMgr->GetConditionsForSmartEvent(e.entryOrGuid, e.event_id, e.source_type); + ConditionSourceInfo info = ConditionSourceInfo(unit, GetBaseObject()); + + if (sConditionMgr->IsObjectMeetToConditions(info, conds)) + ProcessAction(e, unit, var0, var1, bvar, spell, gob); + + RecalcTimer(e, min, max); +} + void SmartScript::InstallTemplate(SmartScriptHolder const& e) { if (!GetBaseObject()) @@ -2121,18 +2132,20 @@ SmartScriptHolder SmartScript::CreateEvent(SMART_EVENT e, uint32 event_flags, ui ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*= NULL*/) { - Unit* trigger = NULL; + Unit* scriptTrigger = NULL; if (invoker) - trigger = invoker; + scriptTrigger = invoker; else if (Unit* tempLastInvoker = GetLastInvoker()) - trigger = tempLastInvoker; + scriptTrigger = tempLastInvoker; + + WorldObject* baseObject = GetBaseObject(); ObjectList* l = new ObjectList(); switch (e.GetTargetType()) { case SMART_TARGET_SELF: - if (GetBaseObject()) - l->push_back(GetBaseObject()); + if (baseObject) + l->push_back(baseObject); break; case SMART_TARGET_VICTIM: if (me && me->getVictim()) @@ -2160,17 +2173,17 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* break; case SMART_TARGET_NONE: case SMART_TARGET_ACTION_INVOKER: - if (trigger) - l->push_back(trigger); + if (scriptTrigger) + l->push_back(scriptTrigger); break; case SMART_TARGET_ACTION_INVOKER_VEHICLE: - if (trigger && trigger->GetVehicle() && trigger->GetVehicle()->GetBase()) - l->push_back(trigger->GetVehicle()->GetBase()); + if (scriptTrigger && scriptTrigger->GetVehicle() && scriptTrigger->GetVehicle()->GetBase()) + l->push_back(scriptTrigger->GetVehicle()->GetBase()); break; case SMART_TARGET_INVOKER_PARTY: - if (trigger) + if (scriptTrigger) { - if (Player* player = trigger->ToPlayer()) + if (Player* player = scriptTrigger->ToPlayer()) { if (Group* group = player->GetGroup()) { @@ -2182,7 +2195,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* // this even if there is a group (thus the else-check), it will add the // same player to the list twice. We don't want that to happen. else - l->push_back(trigger); + l->push_back(scriptTrigger); } } break; @@ -2198,7 +2211,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* if (me && me == *itr) continue; - if (((e.target.unitRange.creature && (*itr)->ToCreature()->GetEntry() == e.target.unitRange.creature) || !e.target.unitRange.creature) && GetBaseObject()->IsInRange(*itr, (float)e.target.unitRange.minDist, (float)e.target.unitRange.maxDist)) + if (((e.target.unitRange.creature && (*itr)->ToCreature()->GetEntry() == e.target.unitRange.creature) || !e.target.unitRange.creature) && baseObject->IsInRange(*itr, (float)e.target.unitRange.minDist, (float)e.target.unitRange.maxDist)) l->push_back(*itr); } @@ -2255,7 +2268,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* if (go && go == *itr) continue; - if (((e.target.goRange.entry && IsGameObject(*itr) && (*itr)->ToGameObject()->GetEntry() == e.target.goRange.entry) || !e.target.goRange.entry) && GetBaseObject()->IsInRange((*itr), (float)e.target.goRange.minDist, (float)e.target.goRange.maxDist)) + if (((e.target.goRange.entry && IsGameObject(*itr) && (*itr)->ToGameObject()->GetEntry() == e.target.goRange.entry) || !e.target.goRange.entry) && baseObject->IsInRange((*itr), (float)e.target.goRange.minDist, (float)e.target.goRange.maxDist)) l->push_back(*itr); } @@ -2265,13 +2278,13 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* case SMART_TARGET_CREATURE_GUID: { Creature* target = NULL; - if (!trigger && !GetBaseObject()) + if (!scriptTrigger && !baseObject) { sLog->outError(LOG_FILTER_SQL, "SMART_TARGET_CREATURE_GUID can not be used without invoker"); break; } - target = FindCreatureNear(trigger ? trigger : GetBaseObject(), e.target.unitGUID.dbGuid); + target = FindCreatureNear(scriptTrigger ? scriptTrigger : baseObject, e.target.unitGUID.dbGuid); if (target && (!e.target.unitGUID.entry || target->GetEntry() == e.target.unitGUID.entry)) l->push_back(target); @@ -2280,13 +2293,13 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* case SMART_TARGET_GAMEOBJECT_GUID: { GameObject* target = NULL; - if (!trigger && !GetBaseObject()) + if (!scriptTrigger && !baseObject) { sLog->outError(LOG_FILTER_SQL, "SMART_TARGET_GAMEOBJECT_GUID can not be used without invoker"); break; } - target = FindGameObjectNear(trigger ? trigger : GetBaseObject(), e.target.goGUID.dbGuid); + target = FindGameObjectNear(scriptTrigger ? scriptTrigger : baseObject, e.target.goGUID.dbGuid); if (target && (!e.target.goGUID.entry || target->GetEntry() == e.target.goGUID.entry)) l->push_back(target); @@ -2296,9 +2309,9 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* { // will always return a valid pointer, even if empty list ObjectList* units = GetWorldObjectsInDist((float)e.target.playerRange.maxDist); - if (!units->empty() && GetBaseObject()) + if (!units->empty() && baseObject) for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); ++itr) - if (IsPlayer(*itr) && GetBaseObject()->IsInRange(*itr, (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist)) + if (IsPlayer(*itr) && baseObject->IsInRange(*itr, (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist)) l->push_back(*itr); delete units; @@ -2325,14 +2338,14 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /* } case SMART_TARGET_CLOSEST_CREATURE: { - Creature* target = GetClosestCreatureWithEntry(GetBaseObject(), e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100), e.target.closest.dead ? false : true); + Creature* target = GetClosestCreatureWithEntry(baseObject, e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100), e.target.closest.dead ? false : true); if (target) l->push_back(target); break; } case SMART_TARGET_CLOSEST_GAMEOBJECT: { - GameObject* target = GetClosestGameObjectWithEntry(GetBaseObject(), e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100)); + GameObject* target = GetClosestGameObjectWithEntry(baseObject, e.target.closest.entry, (float)(e.target.closest.dist ? e.target.closest.dist : 100)); if (target) l->push_back(target); break; @@ -2407,20 +2420,17 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui break; //called from Update tick case SMART_EVENT_UPDATE: - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; case SMART_EVENT_UPDATE_OOC: if (me && me->isInCombat()) return; - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; case SMART_EVENT_UPDATE_IC: if (!me || !me->isInCombat()) return; - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; case SMART_EVENT_HEALT_PCT: { @@ -2429,8 +2439,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui uint32 perc = (uint32)me->GetHealthPct(); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) return; - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; } case SMART_EVENT_TARGET_HEALTH_PCT: @@ -2440,8 +2449,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui uint32 perc = (uint32)me->getVictim()->GetHealthPct(); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) return; - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e, me->getVictim()); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim()); break; } case SMART_EVENT_MANA_PCT: @@ -2451,8 +2459,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui uint32 perc = uint32(100.0f * me->GetPower(POWER_MANA) / me->GetMaxPower(POWER_MANA)); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) return; - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); break; } case SMART_EVENT_TARGET_MANA_PCT: @@ -2462,8 +2469,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui uint32 perc = uint32(100.0f * me->getVictim()->GetPower(POWER_MANA) / me->getVictim()->GetMaxPower(POWER_MANA)); if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min) return; - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - ProcessAction(e, me->getVictim()); + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim()); break; } case SMART_EVENT_RANGE: @@ -2472,18 +2478,15 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui return; if (me->IsInRange(me->getVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max)) - { - ProcessAction(e, me->getVictim()); - RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax); - } + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim()); break; } case SMART_EVENT_TARGET_CASTING: { if (!me || !me->isInCombat() || !me->getVictim() || !me->getVictim()->IsNonMeleeSpellCasted(false, false, true)) return; - ProcessAction(e, me->getVictim()); - RecalcTimer(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax); + + ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->getVictim()); } case SMART_EVENT_FRIENDLY_HEALTH: { @@ -2493,8 +2496,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealt.radius, e.event.friendlyHealt.hpDeficit); if (!target) return; - ProcessAction(e, target); - RecalcTimer(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax); + ProcessTimedAction(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax, target); break; } case SMART_EVENT_FRIENDLY_IS_CC: @@ -2506,8 +2508,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui DoFindFriendlyCC(pList, (float)e.event.friendlyCC.radius); if (pList.empty()) return; - ProcessAction(e, *(pList.begin())); - RecalcTimer(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax); + ProcessTimedAction(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax, *pList.begin()); break; } case SMART_EVENT_FRIENDLY_MISSING_BUFF: @@ -2517,8 +2518,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui if (pList.empty()) return; - ProcessAction(e, *(pList.begin())); - RecalcTimer(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax); + + ProcessTimedAction(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax, *pList.begin()); break; } case SMART_EVENT_HAS_AURA: @@ -2527,10 +2528,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui return; uint32 count = me->GetAuraCount(e.event.aura.spell); if ((!e.event.aura.count && !count) || (e.event.aura.count && count >= e.event.aura.count)) - { - ProcessAction(e); - RecalcTimer(e, e.event.aura.repeatMin, e.event.aura.repeatMax); - } + ProcessTimedAction(e, e.event.aura.repeatMin, e.event.aura.repeatMax); break; } case SMART_EVENT_TARGET_BUFFED: @@ -2540,8 +2538,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui uint32 count = me->getVictim()->GetAuraCount(e.event.aura.spell); if (count < e.event.aura.count) return; - ProcessAction(e); - RecalcTimer(e, e.event.aura.repeatMin, e.event.aura.repeatMax); + ProcessTimedAction(e, e.event.aura.repeatMin, e.event.aura.repeatMax); break; } //no params @@ -2576,10 +2573,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui if (Unit* victim = me->getVictim()) { if (!victim->HasInArc(static_cast<float>(M_PI), me)) - { - ProcessAction(e, victim); - RecalcTimer(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax); - } + ProcessTimedAction(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax, victim); } break; } diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index d0d0221493f..28b328a2947 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -45,6 +45,7 @@ class SmartScript void UpdateTimer(SmartScriptHolder& e, uint32 const diff); void InitTimer(SmartScriptHolder& e); void ProcessAction(SmartScriptHolder& e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL); + void ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL); ObjectList* GetTargets(SmartScriptHolder const& e, Unit* invoker = NULL); ObjectList* GetWorldObjectsInDist(float dist); void InstallTemplate(SmartScriptHolder const& e); diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 01ba222d93b..3c3eded1f68 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -24,10 +24,11 @@ #include "SHA1.h" #include "WorldSession.h" -namespace AccountMgr +AccountMgr::AccountMgr() { +} -AccountOpResult CreateAccount(std::string username, std::string password) +AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password) { if (utf8length(username) > MAX_ACCOUNT_STR) return AOR_NAME_TOO_LONG; // username's too long @@ -52,7 +53,7 @@ AccountOpResult CreateAccount(std::string username, std::string password) return AOR_OK; // everything's fine } -AccountOpResult DeleteAccount(uint32 accountId) +AccountOpResult AccountMgr::DeleteAccount(uint32 accountId) { // Check if accounts exists PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); @@ -124,7 +125,7 @@ AccountOpResult DeleteAccount(uint32 accountId) return AOR_OK; } -AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) +AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) { // Check if accounts exists PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); @@ -154,7 +155,7 @@ AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::s return AOR_OK; } -AccountOpResult ChangePassword(uint32 accountId, std::string newPassword) +AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPassword) { std::string username; @@ -177,7 +178,7 @@ AccountOpResult ChangePassword(uint32 accountId, std::string newPassword) return AOR_OK; } -uint32 GetId(std::string const& username) +uint32 AccountMgr::GetId(std::string const& username) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); stmt->setString(0, username); @@ -186,7 +187,7 @@ uint32 GetId(std::string const& username) return (result) ? (*result)[0].GetUInt32() : 0; } -uint32 GetSecurity(uint32 accountId) +uint32 AccountMgr::GetSecurity(uint32 accountId) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); stmt->setUInt32(0, accountId); @@ -195,7 +196,7 @@ uint32 GetSecurity(uint32 accountId) return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER); } -uint32 GetSecurity(uint32 accountId, int32 realmId) +uint32 AccountMgr::GetSecurity(uint32 accountId, int32 realmId) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); stmt->setUInt32(0, accountId); @@ -205,7 +206,7 @@ uint32 GetSecurity(uint32 accountId, int32 realmId) return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER); } -bool GetName(uint32 accountId, std::string& name) +bool AccountMgr::GetName(uint32 accountId, std::string& name) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); stmt->setUInt32(0, accountId); @@ -220,7 +221,7 @@ bool GetName(uint32 accountId, std::string& name) return false; } -bool CheckPassword(uint32 accountId, std::string password) +bool AccountMgr::CheckPassword(uint32 accountId, std::string password) { std::string username; @@ -238,7 +239,7 @@ bool CheckPassword(uint32 accountId, std::string password) return (result) ? true : false; } -uint32 GetCharactersCount(uint32 accountId) +uint32 AccountMgr::GetCharactersCount(uint32 accountId) { // check character count PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); @@ -248,7 +249,7 @@ uint32 GetCharactersCount(uint32 accountId) return (result) ? (*result)[0].GetUInt64() : 0; } -bool normalizeString(std::string& utf8String) +bool AccountMgr::normalizeString(std::string& utf8String) { wchar_t buffer[MAX_ACCOUNT_STR+1]; @@ -266,7 +267,7 @@ bool normalizeString(std::string& utf8String) return WStrToUtf8(buffer, maxLength, utf8String); } -std::string CalculateShaPassHash(std::string const& name, std::string const& password) +std::string AccountMgr::CalculateShaPassHash(std::string const& name, std::string const& password) { SHA1Hash sha; sha.Initialize(); @@ -278,29 +279,27 @@ std::string CalculateShaPassHash(std::string const& name, std::string const& pas return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength()); } -bool IsPlayerAccount(uint32 gmlevel) +bool AccountMgr::IsPlayerAccount(uint32 gmlevel) { return gmlevel == SEC_PLAYER; } -bool IsModeratorAccount(uint32 gmlevel) +bool AccountMgr::IsModeratorAccount(uint32 gmlevel) { return gmlevel >= SEC_MODERATOR && gmlevel <= SEC_CONSOLE; } -bool IsGMAccount(uint32 gmlevel) +bool AccountMgr::IsGMAccount(uint32 gmlevel) { return gmlevel >= SEC_GAMEMASTER && gmlevel <= SEC_CONSOLE; } -bool IsAdminAccount(uint32 gmlevel) +bool AccountMgr::IsAdminAccount(uint32 gmlevel) { return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE; } -bool IsConsoleAccount(uint32 gmlevel) +bool AccountMgr::IsConsoleAccount(uint32 gmlevel) { return gmlevel == SEC_CONSOLE; } - -} // Namespace AccountMgr diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h index 7154cf0e867..c8de5688e73 100644 --- a/src/server/game/Accounts/AccountMgr.h +++ b/src/server/game/Accounts/AccountMgr.h @@ -21,6 +21,7 @@ #include "Define.h" #include <string> +#include <ace/Singleton.h> enum AccountOpResult { @@ -34,27 +35,34 @@ enum AccountOpResult #define MAX_ACCOUNT_STR 16 -namespace AccountMgr +class AccountMgr { - AccountOpResult CreateAccount(std::string username, std::string password); - AccountOpResult DeleteAccount(uint32 accountId); - AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword); - AccountOpResult ChangePassword(uint32 accountId, std::string newPassword); - bool CheckPassword(uint32 accountId, std::string password); - - uint32 GetId(std::string const& username); - uint32 GetSecurity(uint32 accountId); - uint32 GetSecurity(uint32 accountId, int32 realmId); - bool GetName(uint32 accountId, std::string& name); - uint32 GetCharactersCount(uint32 accountId); - std::string CalculateShaPassHash(std::string const& name, std::string const& password); - - bool normalizeString(std::string& utf8String); - bool IsPlayerAccount(uint32 gmlevel); - bool IsModeratorAccount(uint32 gmlevel); - bool IsGMAccount(uint32 gmlevel); - bool IsAdminAccount(uint32 gmlevel); - bool IsConsoleAccount(uint32 gmlevel); -} + friend class ACE_Singleton<AccountMgr, ACE_Null_Mutex>; + private: + AccountMgr(); + + public: + static AccountOpResult CreateAccount(std::string username, std::string password); + static AccountOpResult DeleteAccount(uint32 accountId); + static AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword); + static AccountOpResult ChangePassword(uint32 accountId, std::string newPassword); + static bool CheckPassword(uint32 accountId, std::string password); + + static uint32 GetId(std::string const& username); + static uint32 GetSecurity(uint32 accountId); + static uint32 GetSecurity(uint32 accountId, int32 realmId); + static bool GetName(uint32 accountId, std::string& name); + static uint32 GetCharactersCount(uint32 accountId); + + static std::string CalculateShaPassHash(std::string const& name, std::string const& password); + static bool normalizeString(std::string& utf8String); + static bool IsPlayerAccount(uint32 gmlevel); + static bool IsModeratorAccount(uint32 gmlevel); + static bool IsGMAccount(uint32 gmlevel); + static bool IsAdminAccount(uint32 gmlevel); + static bool IsConsoleAccount(uint32 gmlevel); +}; + +#define sAccountMgr ACE_Singleton<AccountMgr, ACE_Null_Mutex>::instance() #endif diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index e717a10fcd7..264c2c333aa 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -523,7 +523,7 @@ inline void Battleground::_ProcessJoin(uint32 diff) WorldPacket status; BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(m_TypeID, GetArenaType()); uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId); - sBattlegroundMgr->BuildBattlegroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType(), GetPlayerTeam(itr->first)); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType(), player->GetBGTeam()); player->GetSession()->SendPacket(&status); player->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); @@ -919,7 +919,7 @@ void Battleground::EndBattleground(uint32 winner) player->GetSession()->SendPacket(&pvpLogData); WorldPacket data; - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType(), GetPlayerTeam(player->GetGUID())); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType(), player->GetBGTeam()); player->GetSession()->SendPacket(&data); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1); } @@ -1147,13 +1147,6 @@ void Battleground::AddPlayer(Player* player) sBattlegroundMgr->BuildPlayerJoinedBattlegroundPacket(&data, player); SendPacketToTeam(team, &data, player, false); - // BG Status packet - WorldPacket status; - BattlegroundQueueTypeId bgQueueTypeId = sBattlegroundMgr->BGQueueTypeId(m_TypeID, GetArenaType()); - uint32 queueSlot = player->GetBattlegroundQueueIndex(bgQueueTypeId); - sBattlegroundMgr->BuildBattlegroundStatusPacket(&status, this, queueSlot, STATUS_IN_PROGRESS, 0, GetStartTime(), GetArenaType(), team); - player->GetSession()->SendPacket(&status); - player->RemoveAurasByType(SPELL_AURA_MOUNTED); // add arena specific auras @@ -1846,7 +1839,7 @@ void Battleground::PlayerAddedToBGCheckIfBGIsRunning(Player* player) sBattlegroundMgr->BuildPvpLogDataPacket(&data, this); player->GetSession()->SendPacket(&data); - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType(), GetPlayerTeam(player->GetGUID())); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, this, player->GetBattlegroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType(), player->GetBGTeam()); player->GetSession()->SendPacket(&data); } diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index 368407e7924..f455610666e 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -102,6 +102,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/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 6d291ca301f..249c1696348 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -416,20 +416,17 @@ void ThreatManager::addThreat(Unit* victim, float threat, SpellSchoolMask school void ThreatManager::doAddThreat(Unit* victim, float threat) { - uint32 reducedThreadPercent = victim->GetReducedThreatPercent(); + uint32 redirectThreadPct = victim->GetRedirectThreatPercent(); // must check > 0.0f, otherwise dead loop - if (threat > 0.0f && reducedThreadPercent) + if (threat > 0.0f && redirectThreadPct) { - Unit* redirectTarget = victim->GetMisdirectionTarget(); - if (redirectTarget) - if (Aura* glyphAura = redirectTarget->GetAura(63326)) // Glyph of Vigilance - reducedThreadPercent += glyphAura->GetSpellInfo()->Effects[0].CalcValue(); - - float reducedThreat = threat * reducedThreadPercent / 100.0f; - threat -= reducedThreat; - if (redirectTarget) - _addThreat(redirectTarget, reducedThreat); + if (Unit* redirectTarget = victim->GetRedirectThreatTarget()) + { + float redirectThreat = CalculatePct(threat, redirectThreadPct); + threat -= redirectThreat; + _addThreat(redirectTarget, redirectThreat); + } } _addThreat(victim, threat); diff --git a/src/server/game/Conditions/DisableMgr.cpp b/src/server/game/Conditions/DisableMgr.cpp index fefb51323c4..7e379fdaded 100644 --- a/src/server/game/Conditions/DisableMgr.cpp +++ b/src/server/game/Conditions/DisableMgr.cpp @@ -42,7 +42,7 @@ namespace DisableMap m_DisableMap; - uint8 MAX_DISABLE_TYPES = 7; + uint8 MAX_DISABLE_TYPES = 8; } void LoadDisables() @@ -222,6 +222,34 @@ void LoadDisables() } break; } + case DISABLE_TYPE_MMAP: + { + MapEntry const* mapEntry = sMapStore.LookupEntry(entry); + if (!mapEntry) + { + sLog->outError(LOG_FILTER_SQL, "Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); + continue; + } + switch (mapEntry->map_type) + { + case MAP_COMMON: + sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for world map %u.", entry); + break; + case MAP_INSTANCE: + case MAP_RAID: + sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for instance map %u.", entry); + break; + case MAP_BATTLEGROUND: + sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for battleground map %u.", entry); + break; + case MAP_ARENA: + sLog->outInfo(LOG_FILTER_GENERAL, "Pathfinding disabled for arena map %u.", entry); + break; + default: + break; + } + break; + } default: break; } @@ -350,6 +378,7 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags case DISABLE_TYPE_BATTLEGROUND: case DISABLE_TYPE_OUTDOORPVP: case DISABLE_TYPE_ACHIEVEMENT_CRITERIA: + case DISABLE_TYPE_MMAP: return true; case DISABLE_TYPE_VMAP: return flags & itr->second.flags; diff --git a/src/server/game/Conditions/DisableMgr.h b/src/server/game/Conditions/DisableMgr.h index 89761931048..38751b8a89f 100644 --- a/src/server/game/Conditions/DisableMgr.h +++ b/src/server/game/Conditions/DisableMgr.h @@ -31,7 +31,8 @@ enum DisableType DISABLE_TYPE_BATTLEGROUND = 3, DISABLE_TYPE_ACHIEVEMENT_CRITERIA = 4, DISABLE_TYPE_OUTDOORPVP = 5, - DISABLE_TYPE_VMAP = 6 + DISABLE_TYPE_VMAP = 6, + DISABLE_TYPE_MMAP = 7 }; enum SpellDisableTypes @@ -56,6 +57,11 @@ enum VmapDisableTypes VMAP_DISABLE_LIQUIDSTATUS = 0x8 }; +enum MMapDisableTypes +{ + MMAP_DISABLE_PATHFINDING = 0x0 +}; + namespace DisableMgr { void LoadDisables(); diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 3fdff8e11d6..8a2dad902fa 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -62,6 +62,7 @@ DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEnt DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt); DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt); DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt); +std::map<uint32, CharStartOutfitEntry const*> sCharStartOutfitMap; DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt); DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt); DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt); @@ -237,9 +238,10 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt // sort problematic dbc to (1) non compatible and (2) non-existed if (FILE* f = fopen(dbcFilename.c_str(), "rb")) { - char buf[100]; - snprintf(buf, 100, " exists, and has %u field(s) (expected " SIZEFMTD "). Extracted file might be from wrong client version or a database-update has been forgotten.", storage.GetFieldCount(), strlen(storage.GetFormat())); - errors.push_back(dbcFilename + buf); + std::ostringstream stream; + stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten."; + std::string buf = stream.str(); + errors.push_back(buf); fclose(f); } else @@ -284,6 +286,10 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sBattlemasterListStore, dbcPath, "BattlemasterList.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sBarberShopStyleStore, dbcPath, "BarberShopStyle.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sCharStartOutfitStore, dbcPath, "CharStartOutfit.dbc"); + for (uint32 i = 0; i < sCharStartOutfitStore.GetNumRows(); ++i) + if (CharStartOutfitEntry const* outfit = sCharStartOutfitStore.LookupEntry(i)) + sCharStartOutfitMap[outfit->Race | (outfit->Class << 8) | (outfit->Gender << 16)] = outfit; + LoadDBC(availableDbcLocales, bad_dbc_files, sCharTitlesStore, dbcPath, "CharTitles.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc"); @@ -872,3 +878,11 @@ uint32 GetLiquidFlags(uint32 liquidType) return 0; } +CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender) +{ + std::map<uint32, CharStartOutfitEntry const*>::const_iterator itr = sCharStartOutfitMap.find(race | (class_ << 8) | (gender << 16)); + if (itr == sCharStartOutfitMap.end()) + return NULL; + + return itr->second; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index c77d0ee32f1..48a2cb3fe4e 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -65,6 +65,8 @@ uint32 GetLiquidFlags(uint32 liquidType); PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level); PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id); +CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender); + extern DBCStorage <AchievementEntry> sAchievementStore; extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index aa19863ec29..529c84744bd 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -629,13 +629,13 @@ struct BattlemasterListEntry struct CharStartOutfitEntry { //uint32 Id; // 0 - uint32 RaceClassGender; // 1 (UNIT_FIELD_BYTES_0 & 0x00FFFFFF) comparable (0 byte = race, 1 byte = class, 2 byte = gender) - int32 ItemId[MAX_OUTFIT_ITEMS]; // 2-13 - //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 14-25 not required at server side - //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 26-37 not required at server side - //uint32 Unknown1; // 38, unique values (index-like with gaps ordered in other way as ids) - //uint32 Unknown2; // 39 - //uint32 Unknown3; // 40 + uint8 Race; // 1 + uint8 Class; // 2 + uint8 Gender; // 3 + //uint8 Unused; // 4 + int32 ItemId[MAX_OUTFIT_ITEMS]; // 5-28 + //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 29-52 not required at server side + //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 53-76 not required at server side }; struct CharTitlesEntry diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index b7d966757c8..c82fdf868b3 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -31,7 +31,7 @@ char const AuctionHouseEntryfmt[] = "niiixxxxxxxxxxxxxxxxx"; char const BankBagSlotPricesEntryfmt[] = "ni"; char const BarberShopStyleEntryfmt[] = "nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; char const BattlemasterListEntryfmt[] = "niiiiiiiiixssssssssssssssssxiixx"; -char const CharStartOutfitEntryfmt[] = "xniiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char const CharTitlesEntryfmt[] = "nxssssssssssssssssxxxxxxxxxxxxxxxxxxi"; char const ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; char const ChrClassesEntryfmt[] = "nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 5b3969c9b9d..93d4796f21a 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2148,7 +2148,7 @@ bool Creature::LoadCreaturesAddon(bool reload) if (HasAura(*itr)) { if (!reload) - sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u Entry: %u) has duplicate aura (spell %u) in `auras` field.", GetGUIDLow(), GetEntry(), *itr); + sLog->outError(LOG_FILTER_SQL, "Creature (GUID: %u Entry: %u) has duplicate aura (spell %u) in `auras` field.", GetDBTableGUIDLow(), GetEntry(), *itr); continue; } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 1cd36aec78f..f20d452dc96 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -75,7 +75,6 @@ Object::Object() : m_PackGUID(sizeof(uint64)+1) m_objectType = TYPEMASK_OBJECT; m_uint32Values = NULL; - _changedFields = NULL; m_valuesCount = 0; _fieldNotifyFlags = UF_FLAG_DYNAMIC; @@ -119,8 +118,6 @@ Object::~Object() } delete [] m_uint32Values; - delete [] _changedFields; - } void Object::_InitValues() @@ -128,8 +125,7 @@ void Object::_InitValues() m_uint32Values = new uint32[m_valuesCount]; memset(m_uint32Values, 0, m_valuesCount*sizeof(uint32)); - _changedFields = new bool[m_valuesCount]; - memset(_changedFields, 0, m_valuesCount*sizeof(bool)); + _changesMask.SetCount(m_valuesCount); m_objectUpdated = false; } @@ -757,7 +753,7 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask* void Object::ClearUpdateMask(bool remove) { - memset(_changedFields, 0, m_valuesCount*sizeof(bool)); + _changesMask.Clear(); if (m_objectUpdated) { @@ -854,13 +850,12 @@ void Object::_LoadIntoDataField(std::string const& data, uint32 startOffset, uin for (uint32 index = 0; index < count; ++index) { m_uint32Values[startOffset + index] = atol(tokens[index]); - _changedFields[startOffset + index] = true; + _changesMask.SetBit(startOffset + index); } } void Object::_SetUpdateBits(UpdateMask* updateMask, Player* target) const { - bool* indexes = _changedFields; uint32* flags = NULL; bool isSelf = target == this; bool isOwner = false; @@ -870,8 +865,8 @@ void Object::_SetUpdateBits(UpdateMask* updateMask, Player* target) const GetUpdateFieldData(target, flags, isOwner, isItemOwner, hasSpecialInfo, isPartyMember); - for (uint16 index = 0; index < m_valuesCount; ++index, ++indexes) - if (_fieldNotifyFlags & flags[index] || (flags[index] & UF_FLAG_SPECIAL_INFO && hasSpecialInfo) || (*indexes && IsUpdateFieldVisible(flags[index], isSelf, isOwner, isItemOwner, isPartyMember))) + for (uint16 index = 0; index < m_valuesCount; ++index) + if (_fieldNotifyFlags & flags[index] || (flags[index] & UF_FLAG_SPECIAL_INFO && hasSpecialInfo) || (_changesMask.GetBit(index) && IsUpdateFieldVisible(flags[index], isSelf, isOwner, isItemOwner, isPartyMember))) updateMask->SetBit(index); } @@ -899,7 +894,7 @@ void Object::SetInt32Value(uint16 index, int32 value) if (m_int32Values[index] != value) { m_int32Values[index] = value; - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -916,7 +911,7 @@ void Object::SetUInt32Value(uint16 index, uint32 value) if (m_uint32Values[index] != value) { m_uint32Values[index] = value; - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -931,7 +926,7 @@ void Object::UpdateUInt32Value(uint16 index, uint32 value) ASSERT(index < m_valuesCount || PrintIndexError(index, true)); m_uint32Values[index] = value; - _changedFields[index] = true; + _changesMask.SetBit(index); } void Object::SetUInt64Value(uint16 index, uint64 value) @@ -941,8 +936,8 @@ void Object::SetUInt64Value(uint16 index, uint64 value) { m_uint32Values[index] = PAIR64_LOPART(value); m_uint32Values[index + 1] = PAIR64_HIPART(value); - _changedFields[index] = true; - _changedFields[index + 1] = true; + _changesMask.SetBit(index); + _changesMask.SetBit(index + 1); if (m_inWorld && !m_objectUpdated) { @@ -959,8 +954,8 @@ bool Object::AddUInt64Value(uint16 index, uint64 value) { m_uint32Values[index] = PAIR64_LOPART(value); m_uint32Values[index + 1] = PAIR64_HIPART(value); - _changedFields[index] = true; - _changedFields[index + 1] = true; + _changesMask.SetBit(index); + _changesMask.SetBit(index + 1); if (m_inWorld && !m_objectUpdated) { @@ -981,8 +976,8 @@ bool Object::RemoveUInt64Value(uint16 index, uint64 value) { m_uint32Values[index] = 0; m_uint32Values[index + 1] = 0; - _changedFields[index] = true; - _changedFields[index + 1] = true; + _changesMask.SetBit(index); + _changesMask.SetBit(index + 1); if (m_inWorld && !m_objectUpdated) { @@ -1003,7 +998,7 @@ void Object::SetFloatValue(uint16 index, float value) if (m_floatValues[index] != value) { m_floatValues[index] = value; - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1027,7 +1022,7 @@ void Object::SetByteValue(uint16 index, uint8 offset, uint8 value) { m_uint32Values[index] &= ~uint32(uint32(0xFF) << (offset * 8)); m_uint32Values[index] |= uint32(uint32(value) << (offset * 8)); - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1051,7 +1046,7 @@ void Object::SetUInt16Value(uint16 index, uint8 offset, uint16 value) { m_uint32Values[index] &= ~uint32(uint32(0xFFFF) << (offset * 16)); m_uint32Values[index] |= uint32(uint32(value) << (offset * 16)); - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1118,7 +1113,7 @@ void Object::SetFlag(uint16 index, uint32 newFlag) if (oldval != newval) { m_uint32Values[index] = newval; - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1139,7 +1134,7 @@ void Object::RemoveFlag(uint16 index, uint32 oldFlag) if (oldval != newval) { m_uint32Values[index] = newval; - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1162,7 +1157,7 @@ void Object::SetByteFlag(uint16 index, uint8 offset, uint8 newFlag) if (!(uint8(m_uint32Values[index] >> (offset * 8)) & newFlag)) { m_uint32Values[index] |= uint32(uint32(newFlag) << (offset * 8)); - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1185,7 +1180,7 @@ void Object::RemoveByteFlag(uint16 index, uint8 offset, uint8 oldFlag) if (uint8(m_uint32Values[index] >> (offset * 8)) & oldFlag) { m_uint32Values[index] &= ~uint32(uint32(oldFlag) << (offset * 8)); - _changedFields[index] = true; + _changesMask.SetBit(index); if (m_inWorld && !m_objectUpdated) { @@ -1968,7 +1963,7 @@ void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf) void Object::ForceValuesUpdateAtIndex(uint32 i) { - _changedFields[i] = true; + _changesMask.SetBit(i); if (m_inWorld && !m_objectUpdated) { sObjectAccessor->AddUpdateObject(this); @@ -2688,7 +2683,7 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle) desty = pos.m_positionY + dist * std::sin(angle); // Prevent invalid coordinates here, position is unchanged - if (!Trinity::IsValidMapCoord(destx, desty)) + if (!Trinity::IsValidMapCoord(destx, desty, pos.m_positionZ)) { sLog->outFatal(LOG_FILTER_GENERAL, "WorldObject::MovePosition invalid coordinates X: %f and Y: %f were passed!", destx, desty); return; diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index a9afd074c67..641f9afb154 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -20,7 +20,7 @@ #define _OBJECT_H #include "Common.h" -#include "UpdateFields.h" +#include "UpdateMask.h" #include "UpdateData.h" #include "GridReference.h" #include "ObjectDefines.h" @@ -107,7 +107,6 @@ class ByteBuffer; class WorldSession; class Creature; class Player; -class UpdateMask; class InstanceScript; class GameObject; class TempSummon; @@ -348,7 +347,7 @@ class Object float *m_floatValues; }; - bool* _changedFields; + UpdateMask _changesMask; uint16 m_valuesCount; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 5fee82c5ece..66162142fdc 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1120,7 +1120,7 @@ bool Player::Create(uint32 guidlow, CharacterCreateInfo* createInfo) addActionButton(action_itr->button, action_itr->action, action_itr->type); // original items - if (CharStartOutfitEntry const* oEntry = sCharStartOutfitStore.LookupEntry(RaceClassGender)) + if (CharStartOutfitEntry const* oEntry = GetCharStartOutfitEntry(createInfo->Race, createInfo->Class, createInfo->Gender)) { for (int j = 0; j < MAX_OUTFIT_ITEMS; ++j) { @@ -3432,6 +3432,19 @@ void Player::AddNewMailDeliverTime(time_t deliver_time) } } +void DeleteSpellFromAllPlayers(uint32 spellId) +{ + CharacterDatabaseStatements stmts[2] = {CHAR_DEL_INVALID_SPELL_SPELLS, CHAR_DEL_INVALID_SPELL_TALENTS}; + for (uint8 i = 0; i < 2; i++) + { + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmts[i]); + + stmt->setUInt32(0, spellId); + + CharacterDatabase.Execute(stmt); + } +} + bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); @@ -3442,11 +3455,7 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) { sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL); - - stmt->setUInt32(0, spellId); - - CharacterDatabase.Execute(stmt); + DeleteSpellFromAllPlayers(spellId); } else sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId); @@ -3461,11 +3470,7 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) { sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", spellId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL); - - stmt->setUInt32(0, spellId); - - CharacterDatabase.Execute(stmt); + DeleteSpellFromAllPlayers(spellId); } else sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addTalent: Broken spell #%u learning not allowed.", spellId); @@ -3515,11 +3520,7 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent { sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL); - - stmt->setUInt32(0, spellId); - - CharacterDatabase.Execute(stmt); + DeleteSpellFromAllPlayers(spellId); } else sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId); @@ -3534,11 +3535,7 @@ bool Player::addSpell(uint32 spellId, bool active, bool learning, bool dependent { sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spellId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_SPELL); - - stmt->setUInt32(0, spellId); - - CharacterDatabase.Execute(stmt); + DeleteSpellFromAllPlayers(spellId); } else sLog->outError(LOG_FILTER_SPELLS_AURAS, "Player::addSpell: Broken spell #%u learning not allowed.", spellId); @@ -10735,7 +10732,7 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &des { if (no_space_count) *no_space_count = count; - return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND; + return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED : EQUIP_ERR_ITEM_NOT_FOUND; } if (pItem) @@ -11490,8 +11487,37 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool if (!swap && GetItemByPos(INVENTORY_SLOT_BAG_0, eslot)) return EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE; - // if swap ignore item (equipped also) - InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? eslot : uint8(NULL_SLOT)); + // if we are swapping 2 equiped items, CanEquipUniqueItem check + // should ignore the item we are trying to swap, and not the + // destination item. CanEquipUniqueItem should ignore destination + // item only when we are swapping weapon from bag + uint8 ignore = uint8(NULL_SLOT); + switch (eslot) + { + case EQUIPMENT_SLOT_MAINHAND: + ignore = EQUIPMENT_SLOT_OFFHAND; + break; + case EQUIPMENT_SLOT_OFFHAND: + ignore = EQUIPMENT_SLOT_MAINHAND; + break; + case EQUIPMENT_SLOT_FINGER1: + ignore = EQUIPMENT_SLOT_FINGER2; + break; + case EQUIPMENT_SLOT_FINGER2: + ignore = EQUIPMENT_SLOT_FINGER1; + break; + case EQUIPMENT_SLOT_TRINKET1: + ignore = EQUIPMENT_SLOT_TRINKET2; + break; + case EQUIPMENT_SLOT_TRINKET2: + ignore = EQUIPMENT_SLOT_TRINKET1; + break; + } + + if (ignore == uint8(NULL_SLOT) || pItem != GetItemByPos(INVENTORY_SLOT_BAG_0, ignore)) + ignore = eslot; + + InventoryResult res2 = CanEquipUniqueItem(pItem, swap ? ignore : uint8(NULL_SLOT)); if (res2 != EQUIP_ERR_OK) return res2; @@ -21734,7 +21760,7 @@ void Player::LeaveBattleground(bool teleportToEntryPoint) } } -bool Player::CanJoinToBattleground() const +bool Player::CanJoinToBattleground(Battleground const* /*bg*/) const { // check Deserter debuff if (HasAura(26013)) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 52e81d6d0cd..a69f8c44715 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2219,11 +2219,15 @@ class Player : public Unit, public GridObject<Player> WorldLocation const& GetBattlegroundEntryPoint() const { return m_bgData.joinPos; } void SetBattlegroundEntryPoint(); - void SetBGTeam(uint32 team) { m_bgData.bgTeam = team; } + void SetBGTeam(uint32 team) + { + m_bgData.bgTeam = team; + SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0)); + } uint32 GetBGTeam() const { return m_bgData.bgTeam ? m_bgData.bgTeam : GetTeam(); } void LeaveBattleground(bool teleportToEntryPoint = true); - bool CanJoinToBattleground() const; + bool CanJoinToBattleground(Battleground const* bg) const; bool CanReportAfkDueToLimit(); void ReportedAfkBy(Player* reporter); void ClearAfkReports() { m_bgData.bgAfkReporter.clear(); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fe0a1e88885..948b88d4702 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -256,8 +256,8 @@ Unit::Unit(bool isWorldObject): WorldObject(isWorldObject) m_speed_rate[i] = 1.0f; m_charmInfo = NULL; - m_reducedThreatPercent = 0; - m_misdirectionTargetGUID = 0; + + _redirectThreadInfo = RedirectThreatInfo(); // remove aurastates allowing special moves for (uint8 i = 0; i < MAX_REACTIVE; ++i) @@ -385,10 +385,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(); } @@ -4984,127 +4984,6 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType SendAttackStateUpdate(&dmgInfo); } -bool Unit::HandleHasteAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown) -{ - SpellInfo const* hasteSpell = triggeredByAura->GetSpellInfo(); - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - Unit* target = victim; - int32 basepoints0 = 0; - - switch (hasteSpell->SpellFamilyName) - { - case SPELLFAMILY_ROGUE: - { - switch (hasteSpell->Id) - { - // Blade Flurry - case 13877: - case 33735: - { - target = SelectNearbyTarget(victim); - if (!target) - return false; - basepoints0 = damage; - triggered_spell_id = 22482; - break; - } - } - break; - } - } - - // processed charge only counting case - if (!triggered_spell_id) - return true; - - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - - if (!triggerEntry) - { - sLog->outError(LOG_FILTER_UNITS, "Unit::HandleHasteAuraProc: Spell %u has non-existing triggered spell %u", hasteSpell->Id, triggered_spell_id); - return false; - } - - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id)) - return false; - - if (basepoints0) - CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - else - CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown); - - return true; -} - -bool Unit::HandleSpellCritChanceAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown) -{ - SpellInfo const* triggeredByAuraSpell = triggeredByAura->GetSpellInfo(); - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - Unit* target = victim; - int32 basepoints0 = 0; - - switch (triggeredByAuraSpell->SpellFamilyName) - { - case SPELLFAMILY_MAGE: - { - switch (triggeredByAuraSpell->Id) - { - // Focus Magic - case 54646: - { - Unit* caster = triggeredByAura->GetCaster(); - if (!caster) - return false; - - triggered_spell_id = 54648; - target = caster; - break; - } - } - } - } - - // processed charge only counting case - if (!triggered_spell_id) - return true; - - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - - if (!triggerEntry) - { - sLog->outError(LOG_FILTER_UNITS, "Unit::HandleHasteAuraProc: Spell %u has non-existing triggered spell %u", triggeredByAuraSpell->Id, triggered_spell_id); - return false; - } - - // default case - if (!target || (target != this && !target->isAlive())) - return false; - - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id)) - return false; - - if (basepoints0) - CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - else - CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown); - - return true; -} - //victim may be NULL bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) { @@ -5128,38 +5007,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere { switch (dummySpell->Id) { - // Bloodworms Health Leech - case 50453: - { - if (Unit* owner = GetOwner()) - { - basepoints0 = int32(damage * 1.50f); - target = owner; - triggered_spell_id = 50454; - break; - } - return false; - } - // Eye for an Eye - case 9799: - case 25988: - { - // return damage % to attacker but < 50% own total health - basepoints0 = int32(std::min(CalculatePct(damage, triggerAmount), CountPctFromMaxHealth(50))); - triggered_spell_id = 25997; - break; - } - // Sweeping Strikes - case 18765: - case 35429: - { - target = SelectNearbyTarget(victim); - if (!target) - return false; - - triggered_spell_id = 26654; - break; - } // Unstable Power case 24658: { @@ -5176,67 +5023,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere RemoveAuraFromStack(24662); return true; } - // Adaptive Warding (Frostfire Regalia set) - case 28764: - { - if (!procSpell) - return false; - - // find Mage Armor - if (!GetAuraEffect(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000, 0, 0)) - return false; - - switch (GetFirstSchoolInMask(procSpell->GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - case SPELL_SCHOOL_HOLY: - return false; // ignored - case SPELL_SCHOOL_FIRE: triggered_spell_id = 28765; break; - case SPELL_SCHOOL_NATURE: triggered_spell_id = 28768; break; - case SPELL_SCHOOL_FROST: triggered_spell_id = 28766; break; - case SPELL_SCHOOL_SHADOW: triggered_spell_id = 28769; break; - case SPELL_SCHOOL_ARCANE: triggered_spell_id = 28770; break; - default: - return false; - } - - target = this; - break; - } - // Obsidian Armor (Justice Bearer`s Pauldrons shoulder) - case 27539: - { - if (!procSpell) - return false; - - switch (GetFirstSchoolInMask(procSpell->GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - return false; // ignore - case SPELL_SCHOOL_HOLY: triggered_spell_id = 27536; break; - case SPELL_SCHOOL_FIRE: triggered_spell_id = 27533; break; - case SPELL_SCHOOL_NATURE: triggered_spell_id = 27538; break; - case SPELL_SCHOOL_FROST: triggered_spell_id = 27534; break; - case SPELL_SCHOOL_SHADOW: triggered_spell_id = 27535; break; - case SPELL_SCHOOL_ARCANE: triggered_spell_id = 27540; break; - default: - return false; - } - - target = this; - break; - } - // Mana Leech (Passive) (Priest Pet Aura) - case 28305: - { - // Cast on owner - target = GetOwner(); - if (!target) - return false; - - triggered_spell_id = 34650; - break; - } // Mark of Malice case 33493: { @@ -5427,14 +5213,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } return false; } - // Living Seed - case 48504: - { - triggered_spell_id = 48503; - basepoints0 = triggerAmount; - target = this; - break; - } // Kill command case 58914: { @@ -5594,50 +5372,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } break; } - case 71875: // Item - Black Bruise: Necrotic Touch Proc - case 71877: - { - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 71879; - break; - } - // Item - Shadowmourne Legendary - case 71903: - { - if (!victim || !victim->isAlive() || HasAura(73422)) // cant collect shards while under effect of Chaos Bane buff - return false; - - CastSpell(this, 71905, true, NULL, triggeredByAura); - - // this can't be handled in AuraScript because we need to know victim - Aura const* dummy = GetAura(71905); - if (!dummy || dummy->GetStackAmount() < 10) - return false; - - RemoveAurasDueToSpell(71905); - triggered_spell_id = 71904; - target = victim; - break; - } - // Shadow's Fate (Shadowmourne questline) - case 71169: - { - Unit* caster = triggeredByAura->GetCaster(); - if (caster && caster->GetTypeId() == TYPEID_PLAYER && caster->ToPlayer()->GetQuestStatus(24547) == QUEST_STATUS_INCOMPLETE) - { - CastSpell(caster, 71203, true); - return true; - } - else - return false; - } - // Essence of the Blood Queen - case 70871: - { - basepoints0 = CalculatePct(int32(damage), triggerAmount); - CastCustomSpell(70872, SPELLVALUE_BASE_POINT0, basepoints0, this); - return true; - } case 65032: // Boom aura (321 Boombot) { if (victim->GetEntry() != 33343) // Scrapbot @@ -5650,13 +5384,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere instance->DoCastSpellOnPlayers(65037); // Achievement criteria marker break; } - // Dark Hunger (The Lich King encounter) - case 69383: - { - basepoints0 = CalculatePct(int32(damage), 50); - triggered_spell_id = 69384; - break; - } } break; } @@ -5674,22 +5401,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere triggered_spell_id = 29442; break; } - // Master of Elements - if (dummySpell->SpellIconID == 1920) - { - if (!procSpell) - return false; - - // mana cost save - int32 cost = int32(procSpell->ManaCost + CalculatePct(GetCreateMana(), procSpell->ManaCostPercentage)); - basepoints0 = CalculatePct(cost, triggerAmount); - if (basepoints0 <= 0) - return false; - - target = this; - triggered_spell_id = 29077; - break; - } // Arcane Potency if (dummySpell->SpellIconID == 2120) { @@ -5707,7 +5418,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } break; } - // Hot Streak if (dummySpell->SpellIconID == 2999) { @@ -5730,20 +5440,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere counter->SetAmount(25); return true; } - // Burnout - if (dummySpell->SpellIconID == 2998) - { - if (!procSpell) - return false; - - int32 cost = int32(procSpell->ManaCost + CalculatePct(GetCreateMana(), procSpell->ManaCostPercentage)); - basepoints0 = CalculatePct(cost, triggerAmount); - if (basepoints0 <= 0) - return false; - triggered_spell_id = 44450; - target = this; - break; - } // Incanter's Regalia set (add trigger chance to Mana Shield) if (dummySpell->SpellFamilyFlags[0] & 0x8000) { @@ -5773,29 +5469,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED); return true; } - // Ignite - case 11119: - case 11120: - case 12846: - case 12847: - case 12848: - { - switch (dummySpell->Id) - { - case 11119: basepoints0 = int32(0.04f * damage); break; - case 11120: basepoints0 = int32(0.08f * damage); break; - case 12846: basepoints0 = int32(0.12f * damage); break; - case 12847: basepoints0 = int32(0.16f * damage); break; - case 12848: basepoints0 = int32(0.20f * damage); break; - default: - sLog->outError(LOG_FILTER_UNITS, "Unit::HandleDummyAuraProc: non handled spell id: %u (IG)", dummySpell->Id); - return false; - } - - triggered_spell_id = 12654; - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } // Glyph of Ice Block case 56372: { @@ -5815,25 +5488,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } break; } - // Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings) - case 64411: - { - if (!victim) - return false; - basepoints0 = int32(CalculatePct(damage, 15)); - if (AuraEffect* aurEff = victim->GetAuraEffect(64413, 0, GetGUID())) - { - // The shield can grow to a maximum size of 20, 000 damage absorbtion - aurEff->SetAmount(std::min<int32>(aurEff->GetAmount() + basepoints0, 20000)); - - // Refresh and return to prevent replacing the aura - aurEff->GetBase()->RefreshDuration(); - return true; - } - target = victim; - triggered_spell_id = 64413; - break; - } case 47020: // Enter vehicle XT-002 (Scrapbot) { if (GetTypeId() != TYPEID_UNIT) @@ -5854,16 +5508,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere { switch (dummySpell->Id) { - // Sweeping Strikes - case 12328: - { - target = SelectNearbyTarget(victim); - if (!target) - return false; - - triggered_spell_id = 26654; - break; - } // Victorious case 32216: { @@ -5913,14 +5557,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere target = this; break; } - // Damage Shield - if (dummySpell->SpellIconID == 3214) - { - triggered_spell_id = 59653; - // % of amount blocked - basepoints0 = CalculatePct(int32(GetShieldBlockValue()), triggerAmount); - break; - } // Glyph of Blocking if (dummySpell->Id == 58375) { @@ -5991,31 +5627,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage); return true; } - // Fel Synergy - if (dummySpell->SpellIconID == 3222) - { - target = GetGuardianPet(); - if (!target) - return false; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - triggered_spell_id = 54181; - break; - } switch (dummySpell->Id) { - // Siphon Life - case 63108: - { - if (!damage) - break; - // Glyph of Siphon Life - if (HasAura(56216)) - triggerAmount += triggerAmount / 4; - triggered_spell_id = 63106; - target = this; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - break; - } // Glyph of Shadowflame case 63310: { @@ -6126,24 +5739,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere victim->CastSpell(victim, 57669, true, castItem, triggeredByAura); return true; // no hidden cooldown } - // Divine Aegis - if (dummySpell->SpellIconID == 2820) - { - if (!target) - return false; - - // Multiple effects stack, so let's try to find this aura. - int32 bonus = 0; - if (AuraEffect const* aurEff = target->GetAuraEffect(47753, 0)) - bonus = aurEff->GetAmount(); - - basepoints0 = CalculatePct(int32(damage), triggerAmount) + bonus; - if (basepoints0 > target->getLevel() * 125) - basepoints0 = target->getLevel() * 125; - - triggered_spell_id = 47753; - break; - } // Body and Soul if (dummySpell->SpellIconID == 2218) { @@ -6184,31 +5779,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere target = this; break; } - // Glyph of Prayer of Healing - case 55680: - { - triggered_spell_id = 56161; - - SpellInfo const* GoPoH = sSpellMgr->GetSpellInfo(triggered_spell_id); - if (!GoPoH) - return false; - - int EffIndex = 0; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) - { - if (GoPoH->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) - { - EffIndex = i; - break; - } - } - int32 tickcount = GoPoH->GetMaxDuration() / GoPoH->Effects[EffIndex].Amplitude; - if (!tickcount) - return false; - - basepoints0 = CalculatePct(int32(damage), triggerAmount) / tickcount; - break; - } // Improved Shadowform case 47570: case 47569: @@ -6481,13 +6051,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere triggered_spell_id = isWrathSpell ? 48518 : 48517; break; } - // Living Seed - else if (dummySpell->SpellIconID == 2860) - { - triggered_spell_id = 48504; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - break; - } break; } case SPELLFAMILY_ROGUE: @@ -6508,16 +6071,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere triggered_spell_id = 32747; break; } - case 57934: // Tricks of the Trade - { - Unit* redirectTarget = GetMisdirectionTarget(); - RemoveAura(57934); - if (!redirectTarget) - break; - CastSpell(this, 59628, true); - CastSpell(redirectTarget, 57933, true); - break; - } } switch (dummySpell->SpellIconID) @@ -6636,14 +6189,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere switch (dummySpell->Id) { - case 34477: // Misdirection - { - if (!GetMisdirectionTarget()) - return false; - triggered_spell_id = 35079; // 4 sec buff on self - target = this; - break; - } case 57870: // Glyph of Mend Pet { victim->CastSpell(victim, 57894, true, NULL, NULL, GetGUID()); @@ -6654,18 +6199,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } case SPELLFAMILY_PALADIN: { - // Seal of Righteousness - melee proc dummy (addition ${$MWS*(0.022*$AP+0.044*$SPH)} damage) - if (dummySpell->SpellFamilyFlags[0] & 0x8000000) - { - if (effIndex != 0) - return false; - triggered_spell_id = 25742; - float ap = GetTotalAttackPowerValue(BASE_ATTACK); - int32 holy = SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_HOLY) + - victim->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_HOLY); - basepoints0 = (int32)GetAttackTime(BASE_ATTACK) * int32(ap * 0.022f + 0.044f * holy) / 1000; - break; - } // Light's Beacon - Beacon of Light if (dummySpell->Id == 53651) { @@ -6832,20 +6365,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } break; } - case 25899: // Greater Blessing of Sanctuary - case 20911: // Blessing of Sanctuary - { - target = this; - switch (target->getPowerType()) - { - case POWER_MANA: - triggered_spell_id = 57319; - break; - default: - return false; - } - break; - } // Seal of Vengeance (damage calc on apply aura) case 31801: { @@ -7348,20 +6867,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere target = this; break; } - // Earth Shield - if (dummySpell->SpellFamilyFlags[1] & 0x00000400) - { - // 3.0.8: Now correctly uses the Shaman's own spell critical strike chance to determine the chance of a critical heal. - originalCaster = triggeredByAura->GetCasterGUID(); - target = this; - basepoints0 = triggerAmount; - - // Glyph of Earth Shield - if (AuraEffect* aur = GetAuraEffect(63279, 0)) - AddPct(basepoints0, aur->GetAmount()); - triggered_spell_id = 379; - break; - } // Flametongue Weapon (Passive) if (dummySpell->SpellFamilyFlags[0] & 0x200000) { @@ -7813,114 +7318,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere return true; } -bool Unit::HandleObsModEnergyAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown) -{ - SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); - //uint32 effIndex = triggeredByAura->GetEffIndex(); - //int32 triggerAmount = triggeredByAura->GetAmount(); - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - Unit* target = victim; - int32 basepoints0 = 0; - - switch (dummySpell->SpellFamilyName) - { - case SPELLFAMILY_HUNTER: - { - // Aspect of the Viper - if (dummySpell->SpellFamilyFlags[1] & 0x40000) - { - uint32 maxmana = GetMaxPower(POWER_MANA); - basepoints0 = CalculatePct(maxmana, GetAttackTime(RANGED_ATTACK) / 1000.0f); - target = this; - triggered_spell_id = 34075; - break; - } - break; - } - } - // processed charge only counting case - if (!triggered_spell_id) - return true; - - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - - // Try handle unknown trigger spells - if (!triggerEntry) - { - sLog->outError(LOG_FILTER_UNITS, "Unit::HandleObsModEnergyAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id); - return false; - } - - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id)) - return false; - if (basepoints0) - CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - else - CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown); - return true; -} -bool Unit::HandleModDamagePctTakenAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown) -{ - SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); - //uint32 effIndex = triggeredByAura->GetEffIndex(); - //int32 triggerAmount = triggeredByAura->GetAmount(); - - Item* castItem = triggeredByAura->GetBase()->GetCastItemGUID() && GetTypeId() == TYPEID_PLAYER - ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; - - uint32 triggered_spell_id = 0; - Unit* target = victim; - int32 basepoints0 = 0; - - switch (dummySpell->SpellFamilyName) - { - case SPELLFAMILY_PALADIN: - { - // Blessing of Sanctuary - if (dummySpell->SpellFamilyFlags[0] & 0x10000000) - { - switch (getPowerType()) - { - case POWER_MANA: triggered_spell_id = 57319; break; - default: - return false; - } - } - break; - } - } - // processed charge only counting case - if (!triggered_spell_id) - return true; - - SpellInfo const* triggerEntry = sSpellMgr->GetSpellInfo(triggered_spell_id); - - if (!triggerEntry) - { - sLog->outError(LOG_FILTER_UNITS, "Unit::HandleModDamagePctTakenAuraProc: Spell %u has non-existing triggered spell %u", dummySpell->Id, triggered_spell_id); - return false; - } - - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id)) - return false; - - if (basepoints0) - CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); - else - CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown); - - return true; -} // Used in case when access to whole aura is needed // All procs should be handled like this... @@ -8303,12 +7700,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg RemoveAuraFromStack(auraSpellInfo->Id); return false; } - if (auraSpellInfo->Id == 50720) - { - target = triggeredByAura->GetCaster(); - if (!target) - return false; - } break; case SPELLFAMILY_WARLOCK: { @@ -14452,7 +13843,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u Unit* actionTarget = !isVictim ? target : this; DamageInfo damageInfo = DamageInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL, SPELL_DIRECT_DAMAGE); - ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, NULL, &damageInfo, NULL /*HealInfo*/); + HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); + ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, NULL, &damageInfo, &healInfo); ProcTriggeredList procTriggered; // Fill procTriggered list @@ -14483,6 +13875,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions)) continue; + // AuraScript Hook + if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) + continue; + // Triggered spells not triggering additional spells bool triggered = !(spellProto->AttributesEx3 & SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ? (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false; @@ -14531,15 +13927,21 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u SpellInfo const* spellInfo = i->aura->GetSpellInfo(); uint32 Id = i->aura->GetId(); + AuraApplication* aurApp = i->aura->GetApplicationOfTarget(GetGUID()); + + bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo); + // For players set spell cooldown if need uint32 cooldown = 0; - if (GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) + if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) cooldown = i->spellProcEvent->cooldown; // Note: must SetCantProc(false) before return if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC) SetCantProc(true); + i->aura->CallScriptProcHandlers(aurApp, eventInfo); + // This bool is needed till separate aura effect procs are still here bool handled = false; if (HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) @@ -14558,6 +13960,13 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex); ASSERT(triggeredByAura); + bool prevented = i->aura->CallScriptEffectProcHandlers(triggeredByAura, aurApp, eventInfo); + if (prevented) + { + takeCharges = true; + continue; + } + switch (triggeredByAura->GetAuraType()) { case SPELL_AURA_PROC_TRIGGER_SPELL: @@ -14571,17 +13980,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_PROC_TRIGGER_DAMAGE: { // target has to be valid - if (!target) + if (!eventInfo.GetProcTarget()) break; - sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", triggeredByAura->GetAmount(), spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - SpellNonMeleeDamage damageInfo(this, target, spellInfo->Id, spellInfo->SchoolMask); - uint32 newDamage = SpellDamageBonusDone(target, spellInfo, triggeredByAura->GetAmount(), SPELL_DIRECT_DAMAGE); - newDamage = target->SpellDamageBonusTaken(this, spellInfo, newDamage, SPELL_DIRECT_DAMAGE); - CalculateSpellDamageTaken(&damageInfo, newDamage, spellInfo); - DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb); - SendSpellNonMeleeDamageLog(&damageInfo); - DealSpellDamage(&damageInfo, true); + triggeredByAura->HandleProcTriggerDamageAuraProc(aurApp, eventInfo); // this function is part of the new proc system takeCharges = true; break; } @@ -14594,22 +13996,12 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u break; } case SPELL_AURA_OBS_MOD_POWER: - sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (HandleObsModEnergyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - takeCharges = true; - break; + case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: - sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (HandleModDamagePctTakenAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - takeCharges = true; - break; case SPELL_AURA_MOD_MELEE_HASTE: - { - sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (HandleHasteAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - takeCharges = true; + sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); + takeCharges = true; break; - } case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: { sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); @@ -14681,11 +14073,6 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (triggeredByAura->GetCasterGUID() == target->GetGUID()) takeCharges = true; break; - case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: - sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "ProcDamageAndSpell: casting spell id %u (triggered by %s spell crit chance aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (procSpell && HandleSpellCritChanceAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) - takeCharges = true; - break; // CC Auras which use their amount amount to drop // Are there any more auras which need this? case SPELL_AURA_MOD_CONFUSE: @@ -14725,13 +14112,16 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u takeCharges = true; break; } // switch (triggeredByAura->GetAuraType()) + i->aura->CallScriptAfterEffectProcHandlers(triggeredByAura, aurApp, eventInfo); } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) } // if (!handled) // Remove charge (aura can be removed by triggers) - if (useCharges && takeCharges) + if (prepare && useCharges && takeCharges) i->aura->DropCharge(); + i->aura->CallScriptAfterProcHandlers(aurApp, eventInfo); + if (spellInfo->AttributesEx3 & SPELL_ATTR3_DISABLE_PROC) SetCantProc(false); } @@ -14752,7 +14142,7 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge if (!(*itr)->GetRemoveMode()) if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo)) { - (*itr)->GetBase()->PrepareProcToTrigger(); + (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo); aurasTriggeringProc.push_back(*itr); } } @@ -14764,7 +14154,7 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge { if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo)) { - itr->second->GetBase()->PrepareProcToTrigger(); + itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo); aurasTriggeringProc.push_back(itr->second); } } @@ -14885,7 +14275,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(); @@ -17316,8 +16706,8 @@ void Unit::_ExitVehicle(Position const* exitPosition) SendMessageToSet(&data, false); } - Movement::MoveSplineInit init(*this); - init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); + Movement::MoveSplineInit init(this); + init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), false); init.SetFacing(GetOrientation()); init.SetTransportExit(); init.Launch(); @@ -17754,7 +17144,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 4bcc9c4b823..1a41c25d97c 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -493,6 +493,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, @@ -965,6 +966,28 @@ struct SpellPeriodicAuraLogInfo uint32 createProcExtendMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); +struct RedirectThreatInfo +{ + RedirectThreatInfo() : _targetGUID(0), _threatPct(0) { } + uint64 _targetGUID; + uint32 _threatPct; + + uint64 GetTargetGUID() { return _targetGUID; } + uint32 GetThreatPct() { return _threatPct; } + + void Set(uint64 guid, uint32 pct) + { + _targetGUID = guid; + _threatPct = pct; + } + + void ModifyThreatPct(int32 amount) + { + amount += _threatPct; + _threatPct = uint32(std::max(0, amount)); + } +}; + #define MAX_DECLINED_NAME_CASES 5 struct DeclinedName @@ -1611,7 +1634,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(bool self = false); @@ -2153,13 +2176,12 @@ class Unit : public WorldObject uint32 GetModelForForm(ShapeshiftForm form) const; uint32 GetModelForTotem(PlayerTotemType totemType); - void SetReducedThreatPercent(uint32 pct, uint64 guid) - { - m_reducedThreatPercent = pct; - m_misdirectionTargetGUID = guid; - } - uint32 GetReducedThreatPercent() { return m_reducedThreatPercent; } - Unit* GetMisdirectionTarget() { return m_misdirectionTargetGUID ? GetUnit(*this, m_misdirectionTargetGUID) : NULL; } + // Redirect Threat + void SetRedirectThreat(uint64 guid, uint32 pct) { _redirectThreadInfo.Set(guid, pct); } + void ResetRedirectThreat() { SetRedirectThreat(0, 0); } + void ModifyRedirectThreat(int32 amount) { _redirectThreadInfo.ModifyThreatPct(amount); } + uint32 GetRedirectThreatPercent() { return _redirectThreadInfo.GetThreatPct(); } + Unit* GetRedirectThreatTarget() { return _redirectThreadInfo.GetTargetGUID() ? GetUnit(*this, _redirectThreadInfo.GetTargetGUID()) : NULL; } bool IsAIEnabled, NeedChangeAI; bool CreateVehicleKit(uint32 id, uint32 creatureEntry); @@ -2305,10 +2327,6 @@ class Unit : public WorldObject private: bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent); bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleHasteAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleSpellCritChanceAuraProc(Unit* victim, uint32 damage, AuraEffect* triggredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleObsModEnergyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleModDamagePctTakenAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled); bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown); @@ -2341,8 +2359,7 @@ class Unit : public WorldObject ComboPointHolderSet m_ComboPointHolders; - uint32 m_reducedThreatPercent; - uint64 m_misdirectionTargetGUID; + RedirectThreatInfo _redirectThreadInfo; bool m_cleanupDone; // lock made to not add stuff after cleanup before delete bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index a3be443d014..8f040ab6a5e 100644..100755 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -382,7 +382,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/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 619d819bf9b..30257797470 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2956,10 +2956,9 @@ void ObjectMgr::PlayerCreateInfoAddItemHelper(uint32 race_, uint32 class_, uint3 if (count < -1) sLog->outError(LOG_FILTER_SQL, "Invalid count %i specified on item %u be removed from original player create info (use -1)!", count, itemId); - uint32 RaceClass = (race_) | (class_ << 8); for (uint32 gender = 0; gender < GENDER_NONE; ++gender) { - if (CharStartOutfitEntry const* entry = sCharStartOutfitStore.LookupEntry(RaceClass | (gender << 16))) + if (CharStartOutfitEntry const* entry = GetCharStartOutfitEntry(race_, class_, gender)) { bool found = false; for (uint8 x = 0; x < MAX_OUTFIT_ITEMS; ++x) diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 734a1bfbaad..711e07ef941 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1818,7 +1818,7 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* if (bgOrTemplate->GetTypeID() == BATTLEGROUND_RB && member->InBattlegroundQueue()) return ERR_IN_NON_RANDOM_BG; // check for deserter debuff in case not arena queue - if (bgOrTemplate->GetTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground()) + if (bgOrTemplate->GetTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground(bgOrTemplate)) return ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS; // check if member can join any more battleground queues if (!member->HasFreeBattlegroundQueueId()) diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 99056960893..0213df31630 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -138,7 +138,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) } // check Deserter debuff - if (!_player->CanJoinToBattleground()) + if (!_player->CanJoinToBattleground(bg)) { WorldPacket data; sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); @@ -413,7 +413,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData) if (action == 1 && ginfo.ArenaType == 0) { //if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue - if (!_player->CanJoinToBattleground()) + if (!_player->CanJoinToBattleground(bg)) { //send bg command result to show nice message WorldPacket data2; @@ -453,8 +453,9 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData) _player->CleanupAfterTaxiFlight(); } - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), bg->GetPlayerTeam(_player->GetGUID())); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType(), ginfo.Team); _player->GetSession()->SendPacket(&data); + // remove battleground queue status from BGmgr bgQueue.RemovePlayer(_player->GetGUID(), false); // this is still needed here if battleground "jumping" shouldn't add deserter debuff @@ -466,6 +467,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket &recvData) _player->SetBattlegroundId(bg->GetInstanceID(), bgTypeId); // set the destination team _player->SetBGTeam(ginfo.Team); + // bg->HandleBeforeTeleportToBattleground(_player); sBattlegroundMgr->SendToBattleground(_player, ginfo.IsInvitedToBGInstanceGUID, bgTypeId); // add only in HandleMoveWorldPortAck() @@ -541,7 +543,7 @@ void WorldSession::HandleBattlefieldStatusOpcode(WorldPacket & /*recvData*/) { // this line is checked, i only don't know if GetStartTime is changing itself after bg end! // send status in Battleground - sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType, bg->GetPlayerTeam(_player->GetGUID())); + sBattlegroundMgr->BuildBattlegroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType, _player->GetBGTeam()); SendPacket(&data); continue; } diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index c16a33ad97c..1aadd7f319d 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1342,7 +1342,7 @@ void WorldSession::HandleComplainOpcode(WorldPacket& recvData) recvData >> unk1; // probably language recvData >> unk2; // message type? recvData >> unk3; // probably channel id - recvData >> unk4; // unk random value + recvData >> unk4; // time recvData >> description; // spam description string (messagetype, channel name, player name, message) break; } diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index ee0d4f2a1f2..3308c7bee24 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -126,7 +126,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/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 98bcab81763..6de657b5d91 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -198,13 +198,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid if (!owner->IsValidAttackTarget(TargetUnit)) return; - // Not let attack through obstructions - if (sWorld->getBoolConfig(CONFIG_PET_LOS)) - { - if (!pet->IsWithinLOSInMap(TargetUnit)) - return; - } - pet->ClearUnitState(UNIT_STATE_FOLLOW); // This is true if pet has no target or has target but targets differs. if (pet->getVictim() != TargetUnit || (pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack())) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 0eeb824103c..9e6735efc26 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -18,6 +18,7 @@ #include "Map.h" #include "Battleground.h" +#include "MMapFactory.h" #include "CellImpl.h" #include "DynamicTree.h" #include "GridNotifiers.h" @@ -43,7 +44,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'} }; @@ -71,6 +72,8 @@ Map::~Map() if (!m_scriptSchedule.empty()) sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size()); + + MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId); } bool Map::ExistMap(uint32 mapid, int gx, int gy) @@ -119,6 +122,16 @@ bool Map::ExistVMap(uint32 mapid, int gx, int gy) return true; } +void Map::LoadMMap(int gx, int gy) +{ + bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap((sWorld->GetDataPath() + "mmaps").c_str(), GetId(), gx, gy); + + 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 !! @@ -167,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); } @@ -186,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() @@ -998,8 +1013,8 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) GridMaps[gx][gy]->unloadData(); delete GridMaps[gx][gy]; } - // 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)); @@ -1071,7 +1086,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 26974e4f120..c9686d36a42 100644 --- 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 27a1e0cc432..c2079c22e76 100644 --- 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" @@ -261,6 +262,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/Language.h b/src/server/game/Miscellaneous/Language.h index 292833c3955..1aeb16e9c7e 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -742,7 +742,32 @@ enum TrinityStrings LANG_COMMAND_CREATURESTORAGE_NOTFOUND = 818, LANG_CHANNEL_CITY = 819, - // Room for in-game strings 820-999 not used + + LANG_NPCINFO_GOSSIP = 820, + LANG_NPCINFO_QUESTGIVER = 821, + LANG_NPCINFO_TRAINER_CLASS = 822, + LANG_NPCINFO_TRAINER_PROFESSION = 823, + LANG_NPCINFO_VENDOR_AMMO = 824, + LANG_NPCINFO_VENDOR_FOOD = 825, + LANG_NPCINFO_VENDOR_POISON = 826, + LANG_NPCINFO_VENDOR_REAGENT = 827, + LANG_NPCINFO_REPAIR = 828, + LANG_NPCINFO_FLIGHTMASTER = 829, + LANG_NPCINFO_SPIRITHEALER = 830, + LANG_NPCINFO_SPIRITGUIDE = 831, + LANG_NPCINFO_INNKEEPER = 832, + LANG_NPCINFO_BANKER = 833, + LANG_NPCINFO_PETITIONER = 834, + LANG_NPCINFO_TABARDDESIGNER = 835, + LANG_NPCINFO_BATTLEMASTER = 836, + LANG_NPCINFO_AUCTIONEER = 837, + LANG_NPCINFO_STABLEMASTER = 838, + LANG_NPCINFO_GUILD_BANKER = 839, + LANG_NPCINFO_SPELLCLICK = 840, + LANG_NPCINFO_MAILBOX = 841, + LANG_NPCINFO_PLAYER_VEHICLE = 842, + + // Room for in-game strings 843-999 not used // Level 4 (CLI only commands) LANG_COMMAND_EXIT = 1000, @@ -822,7 +847,11 @@ enum TrinityStrings LANG_MOVEGENS_EFFECT = 1142, LANG_MOVEFLAGS_GET = 1143, LANG_MOVEFLAGS_SET = 1144, - // Room for more level 3 1144-1199 not used + LANG_GROUP_ALREADY_IN_GROUP = 1145, + LANG_GROUP_PLAYER_JOINED = 1146, + LANG_GROUP_NOT_IN_GROUP = 1147, + LANG_GROUP_FULL = 1148, + // Room for more level 3 1149-1199 not used // Debug commands LANG_CINEMATIC_NOT_EXIST = 1200, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 19dfb9df20f..3d8ad37c92d 100644 --- 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> @@ -3533,4 +3534,33 @@ enum PartyResult ERR_PARTY_LFG_TELEPORT_IN_COMBAT = 30 }; +const uint32 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/FollowerRefManager.h b/src/server/game/Movement/FollowerRefManager.h index 2bb31d27227..92904f8e4af 100644 --- a/src/server/game/Movement/FollowerRefManager.h +++ b/src/server/game/Movement/FollowerRefManager.h @@ -29,4 +29,3 @@ class FollowerRefManager : public RefManager<Unit, TargetedMovementGeneratorBase }; #endif - diff --git a/src/server/game/Movement/FollowerReference.cpp b/src/server/game/Movement/FollowerReference.cpp index 2ce23e214d1..30797bbaaca 100644 --- a/src/server/game/Movement/FollowerReference.cpp +++ b/src/server/game/Movement/FollowerReference.cpp @@ -34,4 +34,3 @@ void FollowerReference::sourceObjectDestroyLink() { getSource()->stopFollowing(); } - diff --git a/src/server/game/Movement/FollowerReference.h b/src/server/game/Movement/FollowerReference.h index 9e373d2527e..43ad7e7fa58 100644 --- a/src/server/game/Movement/FollowerReference.h +++ b/src/server/game/Movement/FollowerReference.h @@ -32,4 +32,3 @@ class FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase> void sourceObjectDestroyLink(); }; #endif - diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 277a5721c3d..c9ca7772186 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -87,7 +87,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(); @@ -111,7 +111,7 @@ void MotionMaster::UpdateMotion(uint32 diff) else if (needInitTop()) InitTop(); else if (_cleanFlag & MMCF_RESET) - top()->Reset(*_owner); + top()->Reset(_owner); _cleanFlag &= ~MMCF_RESET; } @@ -132,7 +132,7 @@ void MotionMaster::DirectClean(bool reset) if (needInitTop()) InitTop(); else if (reset) - top()->Reset(*_owner); + top()->Reset(_owner); } void MotionMaster::DelayedClean() @@ -163,7 +163,7 @@ void MotionMaster::DirectExpire(bool reset) else if (needInitTop()) InitTop(); else if (reset) - top()->Reset(*_owner); + top()->Reset(_owner); } void MotionMaster::DelayedExpire() @@ -199,19 +199,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 @@ -248,7 +248,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 { @@ -256,7 +256,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); } } @@ -272,7 +272,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 { @@ -280,22 +280,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); } } @@ -306,7 +306,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(); @@ -320,7 +320,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(); @@ -340,7 +340,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); @@ -370,8 +370,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(); @@ -399,14 +399,14 @@ void MotionMaster::MoveFall(uint32 id /*=0*/) _owner->m_movementInfo.SetFallTime(0); } - Movement::MoveSplineInit init(*_owner); - init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz); + Movement::MoveSplineInit init(_owner); + init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false); 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; @@ -414,16 +414,28 @@ 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); } } +void MotionMaster::MoveCharge(PathGenerator path, float speed, uint32 id) +{ + Vector3 dest = path.GetActualEndPosition(); + + MoveCharge(dest.x, dest.y, dest.z, speed, id); + + Movement::MoveSplineInit init(_owner); + init.MovebyPath(path.GetPath()); + init.SetVelocity(speed); + init.Launch(); +} + void MotionMaster::MoveSeekAssistance(float x, float y, float z) { if (_owner->GetTypeId() == TYPEID_PLAYER) @@ -546,7 +558,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot) else { _needInit[slot] = false; - m->Initialize(*_owner); + m->Initialize(_owner); } } @@ -614,7 +626,7 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const void MotionMaster::InitTop() { - top()->Initialize(*_owner); + top()->Initialize(_owner); _needInit[_top] = false; } @@ -622,7 +634,7 @@ void MotionMaster::DirectDelete(_Ty curr) { if (isStatic(curr)) return; - curr->Finalize(*_owner); + curr->Finalize(_owner); delete curr; } @@ -639,9 +651,9 @@ void MotionMaster::DelayedDelete(_Ty curr) bool MotionMaster::GetDestination(float &x, float &y, float &z) { if (_owner->movespline->Finalized()) - return false; + return false; - const G3D::Vector3& dest = _owner->movespline->FinalDestination(); + G3D::Vector3 const& dest = _owner->movespline->FinalDestination(); x = dest.x; y = dest.y; z = dest.z; diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index f6f58afef22..4b6075aac10 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -26,6 +26,7 @@ class MovementGenerator; class Unit; +class PathGenerator; // Creature Entry ID used for waypoints show, visible only for GMs #define VISUAL_WAYPOINT 1 @@ -154,13 +155,14 @@ 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 MoveCharge(PathGenerator path, float speed = SPEED_CHARGE, uint32 id = EVENT_CHARGE); void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); void MoveJumpTo(float angle, float speedXY, float speedZ); void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP) @@ -199,4 +201,3 @@ class MotionMaster //: private std::stack<MovementGenerator *> uint8 _cleanFlag; }; #endif - diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h index 1c1c38bc72f..39394a75513 100644..100755 --- a/src/server/game/Movement/MovementGenerator.h +++ b/src/server/game/Movement/MovementGenerator.h @@ -33,41 +33,47 @@ 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 &, uint32 time_diff) = 0; + virtual bool Update(Unit*, 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))->DoInitialize(*((T*)&u)); + (static_cast<D*>(this))->DoInitialize(static_cast<T*>(u)); } - void Finalize(Unit &u) + + void Finalize(Unit* u) { //u->AssertIsType<T>(); - (static_cast<D*>(this))->DoFinalize(*((T*)&u)); + (static_cast<D*>(this))->DoFinalize(static_cast<T*>(u)); } - void Reset(Unit &u) + + void Reset(Unit* u) { //u->AssertIsType<T>(); - (static_cast<D*>(this))->DoReset(*((T*)&u)); + (static_cast<D*>(this))->DoReset(static_cast<T*>(u)); } - bool Update(Unit &u, uint32 time_diff) + + bool Update(Unit* u, uint32 time_diff) { //u->AssertIsType<T>(); - return (static_cast<D*>(this))->DoUpdate(*((T*)&u), time_diff); + return (static_cast<D*>(this))->DoUpdate(static_cast<T*>(u), time_diff); } }; @@ -88,4 +94,3 @@ typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGenerato typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry; typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository; #endif - diff --git a/src/server/game/Movement/MovementGeneratorImpl.h b/src/server/game/Movement/MovementGeneratorImpl.h index 2618cadc14f..b77db7b5b9d 100644 --- a/src/server/game/Movement/MovementGeneratorImpl.h +++ b/src/server/game/Movement/MovementGeneratorImpl.h @@ -28,4 +28,3 @@ MovementGeneratorFactory<MOVEMENT_GEN>::Create(void * /*data*/) const return (new MOVEMENT_GEN()); } #endif - diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp index 482c16997a0..72537f0898c 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,100 +31,44 @@ #endif template<class T> -void ConfusedMovementGenerator<T>::DoInitialize(T &unit) +void ConfusedMovementGenerator<T>::DoInitialize(T* unit) { - unit.StopMoving(); - float const wander_distance = 4; - float x = unit.GetPositionX(); - float y = unit.GetPositionY(); - float z = unit.GetPositionZ(); + unit->AddUnitState(UNIT_STATE_CONFUSED); + unit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); + unit->GetPosition(i_x, i_y, i_z); - Map const* map = unit.GetBaseMap(); + if (!unit->isAlive() || unit->IsStopped()) + return; - 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->StopMoving(); + unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE); } template<class T> -void ConfusedMovementGenerator<T>::DoReset(T &unit) +void ConfusedMovementGenerator<T>::DoReset(T* unit) { - i_nextMove = 1; i_nextMoveTime.Reset(0); - unit.StopMoving(); - unit.AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE); + + if (!unit->isAlive() || unit->IsStopped()) + return; + + unit->StopMoving(); + unit->AddUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_CONFUSED_MOVE); } template<class T> -bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff) +bool ConfusedMovementGenerator<T>::DoUpdate(T* unit, 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 { @@ -132,14 +77,25 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff) if (i_nextMoveTime.Passed()) { // start moving - unit.AddUnitState(UNIT_STATE_CONFUSED_MOVE); + unit->AddUnitState(UNIT_STATE_CONFUSED_MOVE); + + float dest = 4.0f * (float)rand_norm() - 2.0f; + + Position pos; + pos.Relocate(i_x, i_y, i_z); + unit->MovePositionToFirstCollision(pos, dest, 0.0f); + + PathGenerator path(unit); + path.SetPathLengthLimit(30.0f); + bool result = path.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ); + if (!result || (path.GetPathType() & PATHFIND_NOPATH)) + { + i_nextMoveTime.Reset(100); + 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(); } @@ -149,25 +105,25 @@ bool ConfusedMovementGenerator<T>::DoUpdate(T &unit, uint32 diff) } template<> -void ConfusedMovementGenerator<Player>::DoFinalize(Player &unit) +void ConfusedMovementGenerator<Player>::DoFinalize(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>::DoFinalize(Creature &unit) +void ConfusedMovementGenerator<Creature>::DoFinalize(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>::DoInitialize(Player &player); -template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature &creature); -template void ConfusedMovementGenerator<Player>::DoReset(Player &player); -template void ConfusedMovementGenerator<Creature>::DoReset(Creature &creature); -template bool ConfusedMovementGenerator<Player>::DoUpdate(Player &player, uint32 diff); -template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature &creature, uint32 diff); - +template void ConfusedMovementGenerator<Player>::DoInitialize(Player*); +template void ConfusedMovementGenerator<Creature>::DoInitialize(Creature*); +template void ConfusedMovementGenerator<Player>::DoReset(Player*); +template void ConfusedMovementGenerator<Creature>::DoReset(Creature*); +template bool ConfusedMovementGenerator<Player>::DoUpdate(Player*, uint32 diff); +template bool ConfusedMovementGenerator<Creature>::DoUpdate(Creature*, uint32 diff); diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h index 20e84f3a97f..da29b8aa12e 100755 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.h @@ -22,25 +22,20 @@ #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 DoInitialize(T &); - void DoFinalize(T &); - void DoReset(T &); - bool DoUpdate(T &, uint32); + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(T*); + bool DoUpdate(T*, 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 d9050e2b76a..216fffbfee1 100755..100644 --- 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" @@ -29,384 +30,163 @@ #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)) + if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) return; - if (!_setMoveData(owner)) - return; + owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); float x, y, z; - if (!_getPoint(owner, x, y, z)) - return; + _getPoint(owner, x, y, z); - owner.AddUnitState(UNIT_STATE_FLEEING_MOVE); + PathGenerator path(owner); + path.SetPathLengthLimit(30.0f); + bool result = path.CalculatePath(x, y, z); + if (!result || (path.GetPathType() & PATHFIND_NOPATH)) + { + i_nextCheckTime.Reset(100); + 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) +void FleeingMovementGenerator<T>::_getPoint(T* owner, float &x, float &y, float &z) { - 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) + float dist_from_caster, angle_to_caster; + if (Unit* fright = ObjectAccessor::GetUnit(*owner, i_frightGUID)) { - 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; - default: - angle = 0.0f; - distance = 0.0f; - break; - } - - temp_x = x + distance * std::cos(angle); - temp_y = y + distance * std::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 * std::cos(angle+static_cast<float>(M_PI/2)), temp_y + 1.0f * std::sin(angle+static_cast<float>(M_PI/2)), z, true); - float new_z_right = _map->GetHeight(owner.GetPhaseMask(), temp_x + 1.0f * std::cos(angle-static_cast<float>(M_PI/2)), temp_y + 1.0f * std::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; - } + dist_from_caster = fright->GetDistance(owner); + if (dist_from_caster > 0.2f) + angle_to_caster = fright->GetAngle(owner); 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)) - { - 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); - } - 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); + dist = frand(0.6f, 1.2f)*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE); + angle = frand(0, 2*static_cast<float>(M_PI)); } - 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); - } - - int8 sign = (float)rand_norm() > 0.5f ? 1 : -1; - i_cur_angle = sign*angle + angle_to_caster; - // current distance - i_last_distance_from_caster = cur_dist; - - return true; + Position pos; + owner->GetFirstCollisionPosition(pos, dist, angle); + x = pos.m_positionX; + y = pos.m_positionY; + z = pos.m_positionZ; } template<class T> -void FleeingMovementGenerator<T>::DoInitialize(T &owner) +void FleeingMovementGenerator<T>::DoInitialize(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; + owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); + owner->AddUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); _setTargetLocation(owner); } template<> -void FleeingMovementGenerator<Creature>::_Init(Creature &owner) +void FleeingMovementGenerator<Player>::DoFinalize(Player* owner) { - if (!&owner) - return; - - //owner.SetTargetGuid(ObjectGuid()); - is_water_ok = owner.canSwim(); - is_land_ok = owner.canWalk(); + owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); + owner->ClearUnitState(UNIT_STATE_FLEEING | UNIT_STATE_FLEEING_MOVE); + owner->StopMoving(); } template<> -void FleeingMovementGenerator<Player>::_Init(Player &) +void FleeingMovementGenerator<Creature>::DoFinalize(Creature* owner) { - is_water_ok = true; - is_land_ok = true; -} - -template<> -void FleeingMovementGenerator<Player>::DoFinalize(Player &owner) -{ - owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); - owner.ClearUnitState(UNIT_STATE_FLEEING|UNIT_STATE_FLEEING_MOVE); - owner.StopMoving(); -} - -template<> -void FleeingMovementGenerator<Creature>::DoFinalize(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>::DoReset(T &owner) +void FleeingMovementGenerator<T>::DoReset(T* owner) { DoInitialize(owner); } template<class T> -bool FleeingMovementGenerator<T>::DoUpdate(T &owner, uint32 time_diff) +bool FleeingMovementGenerator<T>::DoUpdate(T* owner, 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>::DoInitialize(Player &); -template void FleeingMovementGenerator<Creature>::DoInitialize(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>::DoReset(Player &); -template void FleeingMovementGenerator<Creature>::DoReset(Creature &); -template bool FleeingMovementGenerator<Player>::DoUpdate(Player &, uint32); -template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature &, uint32); - -void TimedFleeingMovementGenerator::Finalize(Unit &owner) +template void FleeingMovementGenerator<Player>::DoInitialize(Player*); +template void FleeingMovementGenerator<Creature>::DoInitialize(Creature*); +template void FleeingMovementGenerator<Player>::_getPoint(Player*, float&, float&, float&); +template void FleeingMovementGenerator<Creature>::_getPoint(Creature*, float&, float&, float&); +template void FleeingMovementGenerator<Player>::_setTargetLocation(Player*); +template void FleeingMovementGenerator<Creature>::_setTargetLocation(Creature*); +template void FleeingMovementGenerator<Player>::DoReset(Player*); +template void FleeingMovementGenerator<Creature>::DoReset(Creature*); +template bool FleeingMovementGenerator<Player>::DoUpdate(Player*, uint32); +template bool FleeingMovementGenerator<Creature>::DoUpdate(Creature*, 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, uint32 time_diff) +bool TimedFleeingMovementGenerator::Update(Unit* owner, 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; } @@ -418,4 +198,3 @@ bool TimedFleeingMovementGenerator::Update(Unit & owner, uint32 time_diff) // This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff); } - diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.h index 8ad165c8206..33a7c705564 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 DoInitialize(T &); - void DoFinalize(T &); - void DoReset(T &); - bool DoUpdate(T &, uint32); + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(T*); + bool DoUpdate(T*, 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*); + void _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,12 +50,11 @@ class TimedFleeingMovementGenerator : public FleeingMovementGenerator<Creature> i_totalFleeTime(time) {} MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; } - bool Update(Unit &, uint32); - void Finalize(Unit &); + bool Update(Unit*, uint32); + void Finalize(Unit*); private: TimeTracker i_totalFleeTime; }; #endif - diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp index 4f0a620c303..a94ef5d4f87 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.cpp @@ -23,49 +23,50 @@ #include "MoveSplineInit.h" #include "MoveSpline.h" -void HomeMovementGenerator<Creature>::DoInitialize(Creature & owner) +void HomeMovementGenerator<Creature>::DoInitialize(Creature* owner) { _setTargetLocation(owner); } -void HomeMovementGenerator<Creature>::DoReset(Creature &) +void HomeMovementGenerator<Creature>::DoFinalize(Creature* owner) +{ + if (arrived) + { + owner->ClearUnitState(UNIT_STATE_EVADE); + owner->SetWalk(true); + owner->LoadCreaturesAddon(true); + owner->AI()->JustReachedHome(); + } +} + +void HomeMovementGenerator<Creature>::DoReset(Creature*) { } -void HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner) +void HomeMovementGenerator<Creature>::_setTargetLocation(Creature* owner) { - if (owner.HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED)) + 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); - //} + 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(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE)); -} -bool HomeMovementGenerator<Creature>::DoUpdate(Creature &owner, const uint32 /*time_diff*/) -{ - arrived = owner.movespline->Finalized(); - return !arrived; + owner->ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_EVADE)); } -void HomeMovementGenerator<Creature>::DoFinalize(Creature& owner) +bool HomeMovementGenerator<Creature>::DoUpdate(Creature* owner, const uint32 /*time_diff*/) { - if (arrived) - { - owner.ClearUnitState(UNIT_STATE_EVADE); - owner.SetWalk(true); - owner.LoadCreaturesAddon(true); - owner.AI()->JustReachedHome(); - } + arrived = owner->movespline->Finalized(); + return !arrived; } diff --git a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h index c93241b82db..3d6c6ab18c9 100644 --- a/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/HomeMovementGenerator.h @@ -34,15 +34,14 @@ class HomeMovementGenerator<Creature> : public MovementGeneratorMedium< Creature HomeMovementGenerator() : arrived(false) {} ~HomeMovementGenerator() {} - void DoInitialize(Creature &); - void DoFinalize(Creature &); - void DoReset(Creature &); - bool DoUpdate(Creature &, const uint32); + void DoInitialize(Creature*); + void DoFinalize(Creature*); + void DoReset(Creature*); + bool DoUpdate(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 c952ad9ba94..81442570940 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, uint32 diff) +bool RotateMovementGenerator::Update(Unit* owner, 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, 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, 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*/, uint32 time_diff) +bool DistractMovementGenerator::Update(Unit* /*owner*/, uint32 time_diff) { if (time_diff > m_timer) return false; @@ -98,9 +98,8 @@ bool DistractMovementGenerator::Update(Unit& /*owner*/, 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 bbcc2413cae..0043891db2c 100644..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 &, uint32) { return true; } + void Initialize(Unit*); + void Finalize(Unit*) { } + void Reset(Unit*); + bool Update(Unit*, 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, uint32 time_diff); + void Initialize(Unit*); + void Finalize(Unit*); + void Reset(Unit* owner) { Initialize(owner); } + bool Update(Unit*, 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, uint32 time_diff); + void Initialize(Unit*); + void Finalize(Unit*); + void Reset(Unit* owner) { Initialize(owner); } + bool Update(Unit*, uint32); MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; } private: @@ -72,8 +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 68f62d28899..06b2315f294 100644..100755 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp @@ -27,111 +27,115 @@ //----- Point Movement Generator template<class T> -void PointMovementGenerator<T>::DoInitialize(T &unit) +void PointMovementGenerator<T>::DoInitialize(T* unit) { - if (!unit.IsStopped()) - unit.StopMoving(); + if (!unit->IsStopped()) + unit->StopMoving(); + + unit->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); + + if (id == EVENT_CHARGE) + return; - 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>::DoUpdate(T &unit, uint32 /*diff*/) +bool PointMovementGenerator<T>::DoUpdate(T* unit, 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 (id != EVENT_CHARGE && i_recalculateSpeed && !unit->movespline->Finalized()) { 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) // Default value for point motion type is 0.0, if 0.0 spline will use GetSpeed on unit init.SetVelocity(speed); init.Launch(); } - return !unit.movespline->Finalized(); + return !unit->movespline->Finalized(); } template<class T> -void PointMovementGenerator<T>::DoFinalize(T &unit) +void PointMovementGenerator<T>::DoFinalize(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>::DoReset(T &unit) +void PointMovementGenerator<T>::DoReset(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>::DoInitialize(Player&); -template void PointMovementGenerator<Creature>::DoInitialize(Creature&); -template void PointMovementGenerator<Player>::DoFinalize(Player&); -template void PointMovementGenerator<Creature>::DoFinalize(Creature&); -template void PointMovementGenerator<Player>::DoReset(Player&); -template void PointMovementGenerator<Creature>::DoReset(Creature&); -template bool PointMovementGenerator<Player>::DoUpdate(Player &, uint32); -template bool PointMovementGenerator<Creature>::DoUpdate(Creature&, uint32); +template void PointMovementGenerator<Player>::DoInitialize(Player*); +template void PointMovementGenerator<Creature>::DoInitialize(Creature*); +template void PointMovementGenerator<Player>::DoFinalize(Player*); +template void PointMovementGenerator<Creature>::DoFinalize(Creature*); +template void PointMovementGenerator<Player>::DoReset(Player*); +template void PointMovementGenerator<Creature>::DoReset(Creature*); +template bool PointMovementGenerator<Player>::DoUpdate(Player*, uint32); +template bool PointMovementGenerator<Creature>::DoUpdate(Creature*, 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, uint32) +bool EffectMovementGenerator::Update(Unit* unit, 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 f9a51d0c5f3..421736ca4ec 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), speed(_speed), m_generatePath(_generatePath), i_recalculateSpeed(false) {} - void DoInitialize(T &); - void DoFinalize(T &); - void DoReset(T &); - bool DoUpdate(T &, uint32); + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(T*); + bool DoUpdate(T*, 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,14 +64,13 @@ 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, uint32); + void Initialize(Unit*) {} + void Finalize(Unit*); + void Reset(Unit*) {} + bool Update(Unit*, uint32); MovementGeneratorType GetMovementGeneratorType() { return EFFECT_MOTION_TYPE; } private: uint32 m_Id; }; #endif - diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 06521fadb3a..723b0748494 100644 --- 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>::DoInitialize(Creature &creature) +void RandomMovementGenerator<Creature>::DoInitialize(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>::DoReset(Creature &creature) +void RandomMovementGenerator<Creature>::DoReset(Creature* creature) { DoInitialize(creature); } template<> -void RandomMovementGenerator<Creature>::DoFinalize(Creature &creature) +void RandomMovementGenerator<Creature>::DoFinalize(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>::DoUpdate(Creature &creature, const uint32 diff) +bool RandomMovementGenerator<Creature>::DoUpdate(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>::DoUpdate(Creature &creature, const uint3 } template<> -bool RandomMovementGenerator<Creature>::GetResetPosition(Creature &creature, float& x, float& y, float& z) +bool RandomMovementGenerator<Creature>::GetResetPos(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 3e74753bc91..a6159e995fe 100644 --- 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 DoInitialize(T &); - void DoFinalize(T &); - void DoReset(T &); - bool DoUpdate(T &, const uint32); - bool GetResetPosition(T&, float& x, float& y, float& z); + void _setRandomLocation(T*); + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(T*); + bool DoUpdate(T*, const uint32); + bool GetResetPos(T*, float& x, float& y, float& z); MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; } private: TimeTrackerSmall i_nextMoveTime; @@ -41,4 +41,3 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen float wander_distance; }; #endif - diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index a1ac4ccb679..abb4ac9964b 100755..100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -26,140 +26,116 @@ #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, bool updateDestination) { if (!i_target.isValid() || !i_target->IsInWorld()) return; - if (owner.HasUnitState(UNIT_STATE_NOT_MOVE)) + if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) + return; + + if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature())) return; float x, y, z; - //! Following block of code deleted by MrSmite in issue 4891 - //! Code kept for learning and diagnostical purposes -// -// if (i_offset && i_target->IsWithinDistInMap(&owner, 2*i_offset)) -// { -// if (!owner.movespline->Finalized()) -// return; -// -// owner.GetPosition(x, y, z); -// } -// else - if (!i_offset) - { - if (i_target->IsWithinMeleeRange(&owner)) - return; - // to nearest random contact position - i_target->GetRandomContactPoint(&owner, x, y, z, 0, MELEE_RANGE - 0.5f); - } - else + if (updateDestination || !i_path) { - float dist; - float size; - - // Pets need special handling. - // We need to subtract GetObjectSize() because it gets added back further down the chain - // and that makes pets too far away. Subtracting it allows pets to properly - // be (GetCombatReach() + i_offset) away. - // Only applies when i_target is pet's owner otherwise pets and mobs end up - // doing a "dance" while fighting - if (owner.isPet() && i_target->GetTypeId() == TYPEID_PLAYER) + if (!i_offset) { - dist = i_target->GetCombatReach(); - size = i_target->GetCombatReach() - i_target->GetObjectSize(); + // to nearest contact position + i_target->GetContactPoint(owner, x, y, z); } else { - dist = i_offset + 1.0f; - size = owner.GetObjectSize(); + float dist; + float size; + + // Pets need special handling. + // We need to subtract GetObjectSize() because it gets added back further down the chain + // and that makes pets too far away. Subtracting it allows pets to properly + // be (GetCombatReach() + i_offset) away. + // Only applies when i_target is pet's owner otherwise pets and mobs end up + // doing a "dance" while fighting + if (owner->isPet() && i_target->GetTypeId() == TYPEID_PLAYER) + { + dist = i_target->GetCombatReach(); + size = i_target->GetCombatReach() - i_target->GetObjectSize(); + } + else + { + dist = i_offset + 1.0f; + size = owner->GetObjectSize(); + } + + if (i_target->IsWithinDistInMap(owner, dist)) + return; + + // to at i_offset distance from target and i_angle from target facing + i_target->GetClosePoint(x, y, z, size, i_offset, i_angle); } - - if (i_target->IsWithinDistInMap(&owner, dist)) - return; - - // to at i_offset distance from target and i_angle from target facing - i_target->GetClosePoint(x, y, z, size, i_offset, i_angle); + } + else + { + // the destination has not changed, we just need to refresh the path (usually speed change) + G3D::Vector3 end = i_path->GetEndPosition(); + x = end.x; + y = end.y; + z = end.z; } - /* - 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 + if (!i_path) + i_path = new PathGenerator(owner); - //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; - */ + // allow pets to use shortcut if no path found when following their master + bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->isPet() + && owner->HasUnitState(UNIT_STATE_FOLLOW)); + bool result = i_path->CalculatePath(x, y, z, forceDest); + if (!result || (i_path->GetPathType() & PATHFIND_NOPATH)) + { + // Cant reach target + i_recalculateTravel = true; + 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(); -} - -template<> -void TargetedMovementGeneratorMedium<Player, ChaseMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/) -{ - // nothing to do for Player -} + // Using the same condition for facing target as the one that is used for SetInFront on movement end + // - applies to ChaseMovementGenerator mostly + if (i_angle == 0.f) + init.SetFacing(i_target.getTarget()); -template<> -void TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::UpdateFinalDistance(float /*fDistance*/) -{ - // nothing to do for Player -} - -template<> -void TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance) -{ - i_offset = fDistance; - i_recalculateTravel = true; -} - -template<> -void TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::UpdateFinalDistance(float fDistance) -{ - i_offset = fDistance; - i_recalculateTravel = true; + init.Launch(); } template<class T, typename D> -bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T &owner, uint32 time_diff) +bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff) { if (!i_target.isValid() || !i_target->IsInWorld()) return false; - if (!owner.isAlive()) - return true; + if (!owner || !owner->isAlive()) + return false; - 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; } @@ -170,22 +146,29 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T &owner, uint32 time_diff) return true; } + bool targetMoved = false; 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) - _setTargetLocation(owner); + float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); + G3D::Vector3 dest = owner->movespline->FinalDestination(); + + 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 (owner.movespline->Finalized()) + if (i_recalculateTravel || targetMoved) + _setTargetLocation(owner, targetMoved); + + 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) { @@ -193,60 +176,56 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T &owner, uint32 time_diff) static_cast<D*>(this)->_reachTarget(owner); } } - else - { - if (i_recalculateTravel) - _setTargetLocation(owner); - } + return true; } //-----------------------------------------------// 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>::DoInitialize(Player &owner) +void ChaseMovementGenerator<Player>::DoInitialize(Player* owner) { - owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE); - _setTargetLocation(owner); + owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE); + _setTargetLocation(owner, true); } template<> -void ChaseMovementGenerator<Creature>::DoInitialize(Creature &owner) +void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner) { - owner.SetWalk(false); - owner.AddUnitState(UNIT_STATE_CHASE|UNIT_STATE_CHASE_MOVE); - _setTargetLocation(owner); + owner->SetWalk(false); + owner->AddUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE); + _setTargetLocation(owner, true); } template<class T> -void ChaseMovementGenerator<T>::DoFinalize(T &owner) +void ChaseMovementGenerator<T>::DoFinalize(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>::DoReset(T &owner) +void ChaseMovementGenerator<T>::DoReset(T* owner) { DoInitialize(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()); } //-----------------------------------------------// @@ -263,85 +242,86 @@ bool FollowMovementGenerator<Player>::EnableWalking() const } template<> -void FollowMovementGenerator<Player>::_updateSpeed(Player &/*u*/) +void FollowMovementGenerator<Player>::_updateSpeed(Player* /*owner*/) { // nothing to do for Player } template<> -void FollowMovementGenerator<Creature>::_updateSpeed(Creature &u) +void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner) { // pet only sync speed with owner - if (!((Creature&)u).isPet() || !i_target.isValid() || i_target->GetGUID() != u.GetOwnerGUID()) + /// Make sure we are not in the process of a map change (IsInWorld) + if (!owner->isPet() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID()) return; - u.UpdateSpeed(MOVE_RUN, true); - u.UpdateSpeed(MOVE_WALK, true); - u.UpdateSpeed(MOVE_SWIM, true); + owner->UpdateSpeed(MOVE_RUN, true); + owner->UpdateSpeed(MOVE_WALK, true); + owner->UpdateSpeed(MOVE_SWIM, true); } template<> -void FollowMovementGenerator<Player>::DoInitialize(Player &owner) +void FollowMovementGenerator<Player>::DoInitialize(Player* owner) { - owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE); + owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE); _updateSpeed(owner); - _setTargetLocation(owner); + _setTargetLocation(owner, true); } template<> -void FollowMovementGenerator<Creature>::DoInitialize(Creature &owner) +void FollowMovementGenerator<Creature>::DoInitialize(Creature* owner) { - owner.AddUnitState(UNIT_STATE_FOLLOW|UNIT_STATE_FOLLOW_MOVE); + owner->AddUnitState(UNIT_STATE_FOLLOW | UNIT_STATE_FOLLOW_MOVE); _updateSpeed(owner); - _setTargetLocation(owner); + _setTargetLocation(owner, true); } template<class T> -void FollowMovementGenerator<T>::DoFinalize(T &owner) +void FollowMovementGenerator<T>::DoFinalize(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>::DoReset(T &owner) +void FollowMovementGenerator<T>::DoReset(T* owner) { DoInitialize(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> >::DoUpdate(Player &, uint32); -template bool TargetedMovementGeneratorMedium<Player, FollowMovementGenerator<Player> >::DoUpdate(Player &, uint32); -template bool TargetedMovementGeneratorMedium<Creature, ChaseMovementGenerator<Creature> >::DoUpdate(Creature &, uint32); -template bool TargetedMovementGeneratorMedium<Creature, FollowMovementGenerator<Creature> >::DoUpdate(Creature &, uint32); - -template void ChaseMovementGenerator<Player>::_reachTarget(Player &); -template void ChaseMovementGenerator<Creature>::_reachTarget(Creature &); -template void ChaseMovementGenerator<Player>::DoFinalize(Player &); -template void ChaseMovementGenerator<Creature>::DoFinalize(Creature &); -template void ChaseMovementGenerator<Player>::DoReset(Player &); -template void ChaseMovementGenerator<Creature>::DoReset(Creature &); -template void ChaseMovementGenerator<Player>::MovementInform(Player &unit); - -template void FollowMovementGenerator<Player>::DoFinalize(Player &); -template void FollowMovementGenerator<Creature>::DoFinalize(Creature &); -template void FollowMovementGenerator<Player>::DoReset(Player &); -template void FollowMovementGenerator<Creature>::DoReset(Creature &); -template void FollowMovementGenerator<Player>::MovementInform(Player &unit); +template void TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::_setTargetLocation(Player*, bool); +template void TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::_setTargetLocation(Player*, bool); +template void TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool); +template void TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::_setTargetLocation(Creature*, bool); +template bool TargetedMovementGeneratorMedium<Player,ChaseMovementGenerator<Player> >::DoUpdate(Player*, uint32); +template bool TargetedMovementGeneratorMedium<Player,FollowMovementGenerator<Player> >::DoUpdate(Player*, uint32); +template bool TargetedMovementGeneratorMedium<Creature,ChaseMovementGenerator<Creature> >::DoUpdate(Creature*, uint32); +template bool TargetedMovementGeneratorMedium<Creature,FollowMovementGenerator<Creature> >::DoUpdate(Creature*, uint32); + +template void ChaseMovementGenerator<Player>::_reachTarget(Player*); +template void ChaseMovementGenerator<Creature>::_reachTarget(Creature*); +template void ChaseMovementGenerator<Player>::DoFinalize(Player*); +template void ChaseMovementGenerator<Creature>::DoFinalize(Creature*); +template void ChaseMovementGenerator<Player>::DoReset(Player*); +template void ChaseMovementGenerator<Creature>::DoReset(Creature*); +template void ChaseMovementGenerator<Player>::MovementInform(Player*); + +template void FollowMovementGenerator<Player>::DoFinalize(Player*); +template void FollowMovementGenerator<Creature>::DoFinalize(Creature*); +template void FollowMovementGenerator<Player>::DoReset(Player*); +template void FollowMovementGenerator<Creature>::DoReset(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 4105668838d..3edeb348d54 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,24 +38,24 @@ 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), - i_offset(offset), i_angle(angle), + TargetedMovementGeneratorMedium(Unit* target, float offset, float angle) : + TargetedMovementGeneratorBase(target), i_path(NULL), + i_recheckDistance(0), i_offset(offset), i_angle(angle), i_recalculateTravel(false), i_targetReached(false) { } - ~TargetedMovementGeneratorMedium() {} + ~TargetedMovementGeneratorMedium() { delete i_path; } public: - bool DoUpdate(T &, uint32); + bool DoUpdate(T*, uint32); Unit* GetTarget() const { return i_target.getTarget(); } - void unitSpeedChanged() { i_recalculateTravel=true; } - void UpdateFinalDistance(float fDistance); - + void unitSpeedChanged() { i_recalculateTravel = true; } + bool IsReachable() const { return (i_path) ? (i_path->GetPathType() & PATHFIND_NORMAL) : true; } protected: - void _setTargetLocation(T &); + void _setTargetLocation(T* owner, bool updateDestination); + PathGenerator* i_path; TimeTrackerSmall i_recheckDistance; float i_offset; float i_angle; @@ -66,50 +67,50 @@ 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 DoInitialize(T &); - void DoFinalize(T &); - void DoReset(T &); - void MovementInform(T &); + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(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 DoInitialize(T &); - void DoFinalize(T &); - void DoReset(T &); - void MovementInform(T &); + void DoInitialize(T*); + void DoFinalize(T*); + void DoReset(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* owner); }; #endif diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 4b825544bdf..98f4c9581c6 100644..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().c_str(), 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().c_str(), creature->GetEntry(), creature->GetGUIDLow(), path_id); return; } StartMoveNow(creature); } -void WaypointMovementGenerator<Creature>::DoInitialize(Creature &creature) +void WaypointMovementGenerator<Creature>::DoInitialize(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>::DoFinalize(Creature &creature) +void WaypointMovementGenerator<Creature>::DoFinalize(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>::DoReset(Creature &creature) +void WaypointMovementGenerator<Creature>::DoReset(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>::DoUpdate(Creature &creature, uint32 diff) +bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, 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>::DoUpdate(Creature &creature, uint32 di } 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>::DoUpdate(Creature &creature, uint32 di 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>::GetResetPos(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::DoInitialize(Player &player) +void FlightPathMovementGenerator::DoInitialize(Player* player) { - DoReset(player); + Reset(player); InitEndGridInfo(); } -void FlightPathMovementGenerator::DoFinalize(Player& player) +void FlightPathMovementGenerator::DoFinalize(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::DoReset(Player & player) +void FlightPathMovementGenerator::DoReset(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::DoReset(Player & player) init.Launch(); } -bool FlightPathMovementGenerator::DoUpdate(Player &player, uint32 /*diff*/) +bool FlightPathMovementGenerator::DoUpdate(Player* player, 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().c_str()); - 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().c_str()); + player->GetMap()->ScriptsStart(sEventScripts, eventid, player, player); } } -bool FlightPathMovementGenerator::GetResetPosition(Player&, float& x, float& y, float& z) +bool FlightPathMovementGenerator::GetResetPos(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 319a5c66a03..72650570e12 100644..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 DoInitialize(Creature &); - void DoFinalize(Creature &); - void DoReset(Creature &); - bool DoUpdate(Creature &, uint32 diff); + void DoInitialize(Creature*); + void DoFinalize(Creature*); + void DoReset(Creature*); + bool DoUpdate(Creature*, 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 GetResetPos(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 DoInitialize(Player &); - void DoReset(Player &); - void DoFinalize(Player &); - bool DoUpdate(Player &, uint32); + void DoInitialize(Player*); + void DoReset(Player*); + void DoFinalize(Player*); + bool DoUpdate(Player*, 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 GetResetPos(Player*, float& x, float& y, float& z); void InitEndGridInfo(); void PreloadEndGrid(); @@ -143,4 +143,3 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig uint32 _preloadTargetNode; //! node index where preloading starts }; #endif - diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp new file mode 100644 index 00000000000..dbda4aa2411 --- /dev/null +++ b/src/server/game/Movement/PathGenerator.cpp @@ -0,0 +1,803 @@ +/* + * 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) : + _polyLength(0), _type(PATHFIND_BLANK), + _useStraightPath(false), _forceDestination(false), _pointPathLimit(MAX_POINT_PATH_LENGTH), + _endPosition(Vector3::zero()), _sourceUnit(owner), _navMesh(NULL), _navMeshQuery(NULL) +{ + sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::PathGenerator for %u \n", _sourceUnit->GetGUIDLow()); + + uint32 mapId = _sourceUnit->GetMapId(); + if (MMAP::MMapFactory::IsPathfindingEnabled(mapId)) + { + MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager(); + _navMesh = mmap->GetNavMesh(mapId); + _navMeshQuery = mmap->GetNavMeshQuery(mapId, _sourceUnit->GetInstanceId()); + } + + CreateFilter(); +} + +PathGenerator::~PathGenerator() +{ + sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::~PathGenerator() for %u \n", _sourceUnit->GetGUIDLow()); +} + +bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool forceDest) +{ + float x, y, z; + _sourceUnit->GetPosition(x, y, z); + + if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z)) + return false; + + Vector3 dest(destX, destY, destZ); + SetEndPosition(dest); + + Vector3 start(x, y, z); + SetStartPosition(start); + + _forceDestination = forceDest; + + sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::CalculatePath() for %u \n", _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 (!_navMesh || !_navMeshQuery || _sourceUnit->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) || + !HaveTile(start) || !HaveTile(dest)) + { + BuildShortcut(); + _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); + return true; + } + + UpdateFilter(); + + BuildPolyPath(start, dest); + return true; +} + +dtPolyRef PathGenerator::GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* 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 != _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(float const* 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(_pathPolyRefs, _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 = _navMeshQuery->findNearestPoly(point, extents, &_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 = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint); + if (DT_SUCCESS == result && polyRef != INVALID_POLYREF) + { + *distance = dtVdist(closestPoint, point); + return polyRef; + } + + return INVALID_POLYREF; +} + +void PathGenerator::BuildPolyPath(Vector3 const& startPos, Vector3 const& 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 = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly(); + + bool waterPath = _sourceUnit->GetTypeId() == TYPEID_UNIT && _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 (uint32 i = 0; i < _pathPoints.size(); ++i) + { + ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL); + // One of the points is not in the water, cancel movement. + if (status == LIQUID_MAP_NO_WATER) + { + waterPath = false; + break; + } + } + } + + _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 (_sourceUnit->GetTypeId() == TYPEID_UNIT) + { + Creature* owner = (Creature*)_sourceUnit; + + Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos; + if (_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(); + _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); + return; + } + else + { + float closestPoint[VERTEX_SIZE]; + // we may want to use closestPointOnPolyBoundary instead + if (DT_SUCCESS == _navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)) + { + dtVcopy(endPoint, closestPoint); + SetActualEndPosition(Vector3(endPoint[2], endPoint[0], endPoint[1])); + } + + _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(); + + _pathPolyRefs[0] = startPoly; + _polyLength = 1; + + _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; + sLog->outDebug(LOG_FILTER_MAPS, "++ BuildPolyPath :: path type %d\n", _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 = 0; + uint32 pathEndIndex = 0; + + if (_polyLength) + { + for (; pathStartIndex < _polyLength; ++pathStartIndex) + { + // here to carch few bugs + ASSERT(_pathPolyRefs[pathStartIndex] != INVALID_POLYREF); + + if (_pathPolyRefs[pathStartIndex] == startPoly) + { + startPolyFound = true; + break; + } + } + + for (pathEndIndex = _polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) + if (_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 + + _polyLength = pathEndIndex - pathStartIndex + 1; + memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _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 + + _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(_polyLength * 0.8f + 0.5f); + memmove(_pathPolyRefs, _pathPolyRefs+pathStartIndex, prefixPolyLength * sizeof(dtPolyRef)); + + dtPolyRef suffixStartPoly = _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 != _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 = _pathPolyRefs[prefixPolyLength-1]; + if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)) + { + // suffixStartPoly is still invalid, error state + BuildShortcut(); + _type = PATHFIND_NOPATH; + return; + } + } + + // generate suffix + uint32 suffixPolyLength = 0; + dtStatus dtResult = _navMeshQuery->findPath( + suffixStartPoly, // start polygon + endPoly, // end polygon + suffixEndPoint, // start position + endPoint, // end position + &_filter, // polygon search filter + _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", _sourceUnit->GetGUIDLow()); + } + + sLog->outDebug(LOG_FILTER_MAPS, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength); + + // new path = prefix + suffix - overlap + _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 = _navMeshQuery->findPath( + startPoly, // start polygon + endPoly, // end polygon + startPoint, // start position + endPoint, // end position + &_filter, // polygon search filter + _pathPolyRefs, // [out] path + (int*)&_polyLength, + MAX_PATH_LENGTH); // max number of polygons in output path + + if (!_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", _sourceUnit->GetGUIDLow()); + BuildShortcut(); + _type = PATHFIND_NOPATH; + return; + } + } + + // by now we know what type of path we can get + if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE)) + _type = PATHFIND_NORMAL; + else + _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 (_useStraightPath) + { + dtResult = _navMeshQuery->findStraightPath( + startPoint, // start position + endPoint, // end position + _pathPolyRefs, // current path + _polyLength, // lenth of current path + pathPoints, // [out] path corner points + NULL, // [out] flags + NULL, // [out] shortened path + (int*)&pointCount, + _pointPathLimit); // maximum number of points/polygons to use + } + else + { + dtResult = FindSmoothPath( + startPoint, // start position + endPoint, // end position + _pathPolyRefs, // current path + _polyLength, // length of current path + pathPoints, // [out] path corner points + (int*)&pointCount, + _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(); + _type = PATHFIND_NOPATH; + return; + } + else if (pointCount == _pointPathLimit) + { + sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d\n", pointCount, _pointPathLimit); + BuildShortcut(); + _type = PATHFIND_SHORT; + return; + } + + _pathPoints.resize(pointCount); + for (uint32 i = 0; i < pointCount; ++i) + _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(_pathPoints[pointCount-1]); + + // force the given destination, if needed + if (_forceDestination && + (!(_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()); + _pathPoints[_pathPoints.size()-1] = GetEndPosition(); + } + else + { + SetActualEndPosition(GetEndPosition()); + BuildShortcut(); + } + + _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); + } + + sLog->outDebug(LOG_FILTER_MAPS, "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d\n", _type, pointCount, _polyLength); +} + +void PathGenerator::NormalizePath() +{ + for (uint32 i = 0; i < _pathPoints.size(); ++i) + _sourceUnit->UpdateAllowedPositionZ(_pathPoints[i].x, _pathPoints[i].y, _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 + _pathPoints.resize(2); + + // set start and a default next position + _pathPoints[0] = GetStartPosition(); + _pathPoints[1] = GetActualEndPosition(); + + NormalizePath(); + + _type = PATHFIND_SHORTCUT; +} + +void PathGenerator::CreateFilter() +{ + uint16 includeFlags = 0; + uint16 excludeFlags = 0; + + if (_sourceUnit->GetTypeId() == TYPEID_UNIT) + { + Creature* creature = (Creature*)_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 // assume Player + { + // perfect support not possible, just stay 'safe' + includeFlags |= (NAV_GROUND | NAV_WATER | NAV_MAGMA | NAV_SLIME); + } + + _filter.setIncludeFlags(includeFlags); + _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 (_sourceUnit->IsInWater() || _sourceUnit->IsUnderWater()) + { + uint16 includedFlags = _filter.getIncludeFlags(); + includedFlags |= GetNavTerrain(_sourceUnit->GetPositionX(), + _sourceUnit->GetPositionY(), + _sourceUnit->GetPositionZ()); + + _filter.setIncludeFlags(includedFlags); + } +} + +NavTerrain PathGenerator::GetNavTerrain(float x, float y, float z) +{ + LiquidData data; + _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 = -1, ty = -1; + float point[VERTEX_SIZE] = {p.y, p.z, p.x}; + + _navMesh->calcTileLoc(point, &tx, &ty); + + /// Workaround + /// For some reason, often the tx and ty variables wont get a valid value + /// Use this check to prevent getting negative tile coords and crashing on getTileAt + if (tx < 0 || ty < 0) + return false; + + return (_navMesh->getTileAt(tx, ty) != NULL); +} + +uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* 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 ? 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(float const* startPos, float const* endPos, + float minTargetDist, dtPolyRef const* 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 = _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(float const* startPos, float const* endPos, + dtPolyRef const* 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 != _navMeshQuery->closestPointOnPolyBoundary(polys[0], startPos, iterPos)) + return DT_FAILURE; + + if (DT_SUCCESS != _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; + _navMeshQuery->moveAlongSurface(polys[0], iterPos, moveTgt, &_filter, result, visited, (int*)&nvisited, MAX_VISIT_POLY); + npolys = FixupCorridor(polys, npolys, MAX_PATH_LENGTH, visited, nvisited); + + _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 == _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); + _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(Vector3 const& p1, Vector3 const& 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(Vector3 const& p1, Vector3 const& 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..a20f900b584 --- /dev/null +++ b/src/server/game/Movement/PathGenerator.h @@ -0,0 +1,135 @@ +/* + * 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_GENERATOR_H +#define _PATH_GENERATOR_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 = 0x00, // path not built yet + PATHFIND_NORMAL = 0x01, // normal path + PATHFIND_SHORTCUT = 0x02, // travel through obstacles, terrain, air, etc (old behavior) + PATHFIND_INCOMPLETE = 0x04, // we have partial path to follow - getting closer to target + PATHFIND_NOPATH = 0x08, // no valid path at all or error in generating one + PATHFIND_NOT_USING_PATH = 0x10, // used when we are either flying/swiming or on map w/o mmaps + PATHFIND_SHORT = 0x20, // path is longer or equal to its limited path length +}; + +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) { _useStraightPath = useStraightPath; }; + void SetPathLengthLimit(float distance) { _pointPathLimit = std::min<uint32>(uint32(distance/SMOOTH_PATH_STEP_SIZE), MAX_POINT_PATH_LENGTH); }; + + // result getters + Vector3 const& GetStartPosition() const { return _startPosition; } + Vector3 const& GetEndPosition() const { return _endPosition; } + Vector3 const& GetActualEndPosition() const { return _actualEndPosition; } + + PointsArray& GetPath() { return _pathPoints; } + PathType GetPathType() const { return _type; } + + private: + + dtPolyRef _pathPolyRefs[MAX_PATH_LENGTH]; // array of detour polygon references + uint32 _polyLength; // number of polygons in the path + + PointsArray _pathPoints; // our actual (x,y,z) path to the target + PathType _type; // tells what kind of path this is + + bool _useStraightPath; // type of path will be generated + bool _forceDestination; // when set, we will always arrive at given point + uint32 _pointPathLimit; // limit point path size; min(this, MAX_POINT_PATH_LENGTH) + + Vector3 _startPosition; // {x, y, z} of current location + Vector3 _endPosition; // {x, y, z} of the destination + Vector3 _actualEndPosition;// {x, y, z} of the closest possible point to given destination + + Unit const* const _sourceUnit; // the unit that is moving + dtNavMesh const* _navMesh; // the nav mesh + dtNavMeshQuery const* _navMeshQuery; // the nav mesh query used to find the path + + dtQueryFilter _filter; // use single filter for all movements, update it when needed + + void SetStartPosition(Vector3 Point) { _startPosition = Point; } + void SetEndPosition(Vector3 Point) { _actualEndPosition = Point; _endPosition = Point; } + void SetActualEndPosition(Vector3 Point) { _actualEndPosition = Point; } + void NormalizePath(); + + void Clear() + { + _polyLength = 0; + _pathPoints.clear(); + } + + bool InRange(Vector3 const& p1, Vector3 const& p2, float r, float h) const; + float Dist3DSqr(Vector3 const& p1, Vector3 const& p2) const; + bool InRangeYZX(float const* v1, float const* v2, float r, float h) const; + + dtPolyRef GetPathPolyByPosition(dtPolyRef const* polyPath, uint32 polyPathSize, float const* Point, float* Distance = NULL) const; + dtPolyRef GetPolyByLocation(float const* Point, float* Distance) const; + bool HaveTile(Vector3 const& p) const; + + void BuildPolyPath(Vector3 const& startPos, Vector3 const& endPos); + void BuildPointPath(float const* startPoint, float const* 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, dtPolyRef const* visited, uint32 nvisited); + bool GetSteerTarget(float const* startPos, float const* endPos, float minTargetDist, dtPolyRef const* path, uint32 pathSize, float* steerPos, + unsigned char& steerPosFlag, dtPolyRef& steerPosRef); + dtStatus FindSmoothPath(float const* startPos, float const* endPos, + dtPolyRef const* 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 8e51e2678dd..b18166ea615 100644 --- a/src/server/game/Movement/Spline/MoveSpline.h +++ b/src/server/game/Movement/Spline/MoveSpline.h @@ -78,18 +78,17 @@ 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; } public: + int32 Duration() const { return spline.length(); } const MySpline& _Spline() const { return spline; } int32 _currentSplineIdx() const { return point_Idx; } void _Finalize(); void _Interrupt() { splineflags.done = true;} public: - 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 32658d8fdc5..1e4d4950ea0 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.cpp +++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp @@ -58,38 +58,38 @@ 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 // Don't compute for transport movement if the unit is in a motion between two transports if (!move_spline.Finalized() && move_spline.onTransport == transport) 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; move_spline.onTransport = transport; - uint32 moveFlags = unit.m_movementInfo.GetMovementFlags(); + uint32 moveFlags = unit->m_movementInfo.GetMovementFlags(); if (args.flags.walkmode) moveFlags |= MOVEMENTFLAG_WALKING; else @@ -98,36 +98,38 @@ 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(&unit)) - return; + if (!args.Validate(unit)) + 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) @@ -140,9 +142,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(); } @@ -150,8 +152,19 @@ namespace Movement args.flags.EnableFacingAngle(); } - void MoveSplineInit::MoveTo(Vector3 const& dest) + void MoveSplineInit::MoveTo(const Vector3& dest, bool generatePath, bool forceDestination) { + if (generatePath) + { + PathGenerator path(unit); + bool result = path.CalculatePath(dest.x, dest.y, dest.z, forceDestination); + if (result && path.GetPathType() & ~PATHFIND_NOPATH) + { + MovebyPath(path.GetPath()); + return; + } + } + args.path_Idx_offset = 0; args.path.resize(2); TransportPathTransform transform(unit, args.TransformForTransport); @@ -162,7 +175,7 @@ namespace Movement { if (_transformForTransport) { - if (TransportBase* transport = _owner.GetDirectTransport()) + if (TransportBase* transport = _owner->GetDirectTransport()) { float unused = 0.0f; // need reference 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 2188c05f2f3..441bad66142 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 @@ -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,9 +159,9 @@ 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) { - MoveTo(G3D::Vector3(x, y, z)); + MoveTo(G3D::Vector3(x, y, z), 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 3f78a640384..39f05184cf6 100644 --- 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,9 @@ 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/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h index 07f855380f9..b17714f1a51 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.h +++ b/src/server/game/Movement/Waypoints/WaypointManager.h @@ -68,4 +68,3 @@ class WaypointMgr #define sWaypointMgr ACE_Singleton<WaypointMgr, ACE_Null_Mutex>::instance() #endif - diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 3e30e95639f..3da9ac8fade 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -76,6 +76,7 @@ void AddSC_tele_commandscript(); void AddSC_ticket_commandscript(); void AddSC_titles_commandscript(); void AddSC_wp_commandscript(); +void AddSC_mmaps_commandscript(); #ifdef SCRIPTS //world @@ -705,6 +706,7 @@ void AddCommandScripts() AddSC_ticket_commandscript(); AddSC_titles_commandscript(); AddSC_wp_commandscript(); + AddSC_mmaps_commandscript(); } void AddWorldScripts() diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 990963b8c4d..6a26dafde79 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -95,17 +95,28 @@ bool WorldSessionFilter::Process(WorldPacket* packet) /// WorldSession constructor WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): -m_muteTime(mute_time), m_timeOutTime(0), _player(NULL), m_Socket(sock), -_security(sec), _accountId(id), m_expansion(expansion), _logoutTime(0), -m_inQueue(false), m_playerLoading(false), m_playerLogout(false), -m_playerRecentlyLogout(false), m_playerSave(false), -m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)), -m_sessionDbLocaleIndex(locale), -m_latency(0), m_TutorialsChanged(false), recruiterId(recruiter), -isRecruiter(isARecruiter), timeLastWhoCommand(0) + m_muteTime(mute_time), + m_timeOutTime(0), + _player(NULL), + m_Socket(sock), + _security(sec), + _accountId(id), + m_expansion(expansion), + _warden(NULL), + _logoutTime(0), + m_inQueue(false), + m_playerLoading(false), + m_playerLogout(false), + m_playerRecentlyLogout(false), + m_playerSave(false), + m_sessionDbcLocale(sWorld->GetAvailableDbcLocale(locale)), + m_sessionDbLocaleIndex(locale), + m_latency(0), + m_TutorialsChanged(false), + recruiterId(recruiter), + isRecruiter(isARecruiter), + timeLastWhoCommand(0) { - _warden = NULL; - if (sock) { m_Address = sock->GetRemoteAddress(); @@ -727,8 +738,8 @@ void WorldSession::SetAccountData(AccountDataType type, time_t tm, std::string c void WorldSession::SendAccountDataTimes(uint32 mask) { - WorldPacket data(SMSG_ACCOUNT_DATA_TIMES, 4 + 1 + 4 + 8 * 4); // changed in WotLK - data << uint32(time(NULL)); // unix time of something + WorldPacket data(SMSG_ACCOUNT_DATA_TIMES, 4 + 1 + 4 + NUM_ACCOUNT_DATA_TYPES * 4); + data << uint32(time(NULL)); // Server time data << uint8(1); data << uint32(mask); // type mask for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i) diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 39f5425d9df..abe048279db 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -181,9 +181,6 @@ class CharacterCreateInfo /// Server side data uint8 CharCount; - - private: - virtual ~CharacterCreateInfo(){}; }; /// Player session in the World diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index b47f801ab29..c80d25be139 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -159,27 +159,29 @@ int WorldSocket::SendPacket(WorldPacket const& pct) if (closing_) return -1; - // Dump outgoing packet. + // Dump outgoing packet if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(pct, SERVER_TO_CLIENT); + WorldPacket const* pkt = &pct; + + if (m_Session) - sLog->outTrace(LOG_FILTER_OPCODES, "S->C %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pct.GetOpcode()).c_str()); + sLog->outTrace(LOG_FILTER_OPCODES, "S->C: %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pkt->GetOpcode()).c_str()); - // Create a copy of the original packet; this is to avoid issues if a hook modifies it. - sScriptMgr->OnPacketSend(this, WorldPacket(pct)); + sScriptMgr->OnPacketSend(this, *pkt); - ServerPktHeader header(pct.size()+2, pct.GetOpcode()); + ServerPktHeader header(pkt->size()+2, pkt->GetOpcode()); m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); - if (m_OutBuffer->space() >= pct.size() + header.getHeaderLength() && msg_queue()->is_empty()) + if (m_OutBuffer->space() >= pkt->size() + header.getHeaderLength() && msg_queue()->is_empty()) { // Put the packet on the buffer. if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1) ACE_ASSERT (false); - if (!pct.empty()) - if (m_OutBuffer->copy((char*) pct.contents(), pct.size()) == -1) + if (!pkt->empty()) + if (m_OutBuffer->copy((char*) pkt->contents(), pkt->size()) == -1) ACE_ASSERT (false); } else @@ -187,12 +189,12 @@ int WorldSocket::SendPacket(WorldPacket const& pct) // Enqueue the packet. ACE_Message_Block* mb; - ACE_NEW_RETURN(mb, ACE_Message_Block(pct.size() + header.getHeaderLength()), -1); + ACE_NEW_RETURN(mb, ACE_Message_Block(pkt->size() + header.getHeaderLength()), -1); mb->copy((char*) header.header, header.getHeaderLength()); - if (!pct.empty()) - mb->copy((const char*)pct.contents(), pct.size()); + if (!pkt->empty()) + mb->copy((const char*)pkt->contents(), pkt->size()); if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) { @@ -245,20 +247,7 @@ int WorldSocket::open (void *a) m_Address = remote_addr.get_host_addr(); - // Send startup packet. - WorldPacket packet (SMSG_AUTH_CHALLENGE, 24); - packet << uint32(1); // 1...31 - packet << m_Seed; - - BigNumber seed1; - seed1.SetRand(16 * 8); - packet.append(seed1.AsByteArray(16), 16); // new encryption seeds - - BigNumber seed2; - seed2.SetRand(16 * 8); - packet.append(seed2.AsByteArray(16), 16); // new encryption seeds - - if (SendPacket(packet) == -1) + if (HandleSendAuthSession() == -1) return -1; // Register with ACE Reactor @@ -461,7 +450,7 @@ int WorldSocket::Update (void) int ret; do - ret = handle_output (get_handle()); + ret = handle_output(get_handle()); while (ret > 0); return ret; @@ -469,18 +458,18 @@ int WorldSocket::Update (void) int WorldSocket::handle_input_header (void) { - ACE_ASSERT (m_RecvWPct == NULL); + ACE_ASSERT(m_RecvWPct == NULL); - ACE_ASSERT (m_Header.length() == sizeof(ClientPktHeader)); + ACE_ASSERT(m_Header.length() == sizeof(ClientPktHeader)); - m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr(), sizeof(ClientPktHeader)); + m_Crypt.DecryptRecv ((uint8*)m_Header.rd_ptr(), sizeof(ClientPktHeader)); - ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr()); + ClientPktHeader& header = *((ClientPktHeader*)m_Header.rd_ptr()); EndianConvertReverse(header.size); EndianConvert(header.cmd); - if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240)) + if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240)) { Player* _player = m_Session ? m_Session->GetPlayer() : NULL; sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)", @@ -495,11 +484,11 @@ int WorldSocket::handle_input_header (void) header.size -= 4; - ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1); + ACE_NEW_RETURN(m_RecvWPct, WorldPacket ((uint16)header.cmd, header.size), -1); if (header.size > 0) { - m_RecvWPct->resize (header.size); + m_RecvWPct->resize(header.size); m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); } else @@ -666,7 +655,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) ACE_ASSERT (new_pct); // manage memory ;) - ACE_Auto_Ptr<WorldPacket> aptr (new_pct); + ACE_Auto_Ptr<WorldPacket> aptr(new_pct); const ACE_UINT16 opcode = new_pct->GetOpcode(); @@ -677,15 +666,16 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(*new_pct, CLIENT_TO_SERVER); + std::string opcodeName = GetOpcodeNameForLogging(opcode); if (m_Session) - sLog->outTrace(LOG_FILTER_OPCODES, "C->S %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(new_pct->GetOpcode()).c_str()); + sLog->outTrace(LOG_FILTER_OPCODES, "C->S: %s %s", m_Session->GetPlayerInfo().c_str(), opcodeName.c_str()); try { switch (opcode) { case CMSG_PING: - return HandlePing (*new_pct); + return HandlePing(*new_pct); case CMSG_AUTH_SESSION: if (m_Session) { @@ -694,18 +684,17 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) } sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); - return HandleAuthSession (*new_pct); + return HandleAuthSession(*new_pct); case CMSG_KEEP_ALIVE: - sLog->outDebug(LOG_FILTER_NETWORKIO, "%s", GetOpcodeNameForLogging(opcode).c_str()); + sLog->outDebug(LOG_FILTER_NETWORKIO, "%s", opcodeName.c_str()); sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); return 0; default: { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - + ACE_GUARD_RETURN(LockType, Guard, m_SessionLock, -1); if (!m_Session) { - sLog->outError(LOG_FILTER_NETWORKIO, "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + sLog->outError(LOG_FILTER_OPCODES, "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); return -1; } @@ -715,7 +704,7 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) // OK, give the packet to WorldSession aptr.release(); - // WARNINIG here we call it with locks held. + // WARNING here we call it with locks held. // Its possible to cause deadlock if QueuePacket calls back m_Session->QueuePacket(new_pct); return 0; @@ -724,8 +713,8 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) } catch (ByteBufferException &) { - sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet (opcode: %u) from client %s, accountid=%i. Disconnected client.", - opcode, GetRemoteAddress().c_str(), m_Session ? int32(m_Session->GetAccountId()) : -1); + sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet %s from client %s, accountid=%i. Disconnected client.", + opcodeName.c_str(), GetRemoteAddress().c_str(), m_Session ? int32(m_Session->GetAccountId()) : -1); new_pct->hexlike(); return -1; } @@ -733,36 +722,47 @@ int WorldSocket::ProcessIncoming(WorldPacket* new_pct) ACE_NOTREACHED (return 0); } +int WorldSocket::HandleSendAuthSession() +{ + WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); + packet << uint32(1); // 1...31 + packet << uint32(m_Seed); + + BigNumber seed1; + seed1.SetRand(16 * 8); + packet.append(seed1.AsByteArray(16), 16); // new encryption seeds + + BigNumber seed2; + seed2.SetRand(16 * 8); + packet.append(seed2.AsByteArray(16), 16); // new encryption seeds + return SendPacket(packet); +} + int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) { - // NOTE: ATM the socket is singlethread, have this in mind ... uint8 digest[20]; uint32 clientSeed; - uint32 unk2, unk3, unk5, unk6, unk7; - uint64 unk4; - uint32 BuiltNumberClient; - uint32 id, security; - //uint8 expansion = 0; + uint8 security; + uint32 id; LocaleConstant locale; std::string account; SHA1Hash sha; + uint32 clientBuild; + uint32 unk2, unk3, unk5, unk6, unk7; + uint64 unk4; BigNumber v, s, g, N; WorldPacket packet, SendAddonPacked; - BigNumber k; if (sWorld->IsClosed()) { - packet.Initialize(SMSG_AUTH_RESPONSE, 1); - packet << uint8(AUTH_REJECT); - SendPacket(packet); - + SendAuthResponseError(AUTH_REJECT); sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteAddress().c_str()); return -1; } // Read the content of the packet - recvPacket >> BuiltNumberClient; // for now no use + recvPacket >> clientBuild; recvPacket >> unk2; recvPacket >> account; recvPacket >> unk3; @@ -772,7 +772,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) recvPacket.read(digest, 20); sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", - BuiltNumberClient, + clientBuild, unk2, account.c_str(), unk3, @@ -788,11 +788,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Stop if the account is not found if (!result) { - packet.Initialize (SMSG_AUTH_RESPONSE, 1); - packet << uint8 (AUTH_UNKNOWN_ACCOUNT); - - SendPacket(packet); - + SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); return -1; } @@ -810,37 +806,30 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) v.SetHexStr(fields[4].GetCString()); s.SetHexStr (fields[5].GetCString()); - const char* sStr = s.AsHexStr(); //Must be freed by OPENSSL_free() - const char* vStr = v.AsHexStr(); //Must be freed by OPENSSL_free() + const char* sStr = s.AsHexStr(); // Must be freed by OPENSSL_free() + const char* vStr = v.AsHexStr(); // Must be freed by OPENSSL_free() sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: (s, v) check s: %s v: %s", - sStr, - vStr); + sStr, + vStr); - OPENSSL_free ((void*) sStr); - OPENSSL_free ((void*) vStr); + OPENSSL_free((void*)sStr); + OPENSSL_free((void*)vStr); ///- Re-check ip locking (same check as in realmd). if (fields[3].GetUInt8() == 1) // if ip is locked { if (strcmp (fields[2].GetCString(), GetRemoteAddress().c_str())) { - packet.Initialize (SMSG_AUTH_RESPONSE, 1); - packet << uint8 (AUTH_FAILED); - SendPacket(packet); - + SendAuthResponseError(AUTH_FAILED); sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs)."); return -1; } } id = fields[0].GetUInt32(); - /* - if (security > SEC_ADMINISTRATOR) // prevent invalid security settings in DB - security = SEC_ADMINISTRATOR; - */ - k.SetHexStr (fields[1].GetCString()); + k.SetHexStr(fields[1].GetCString()); int64 mutetime = fields[7].GetInt64(); //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. @@ -866,10 +855,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Must be done before WorldSession is created if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED) && os != "Win" && os != "OSX") { - packet.Initialize(SMSG_AUTH_RESPONSE, 1); - packet << uint8(AUTH_REJECT); - SendPacket(packet); - + SendAuthResponseError(AUTH_REJECT); sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", GetRemoteAddress().c_str(), os.c_str()); return -1; } @@ -900,10 +886,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) if (banresult) // if account banned { - packet.Initialize (SMSG_AUTH_RESPONSE, 1); - packet << uint8 (AUTH_BANNED); - SendPacket(packet); - + SendAuthResponseError(AUTH_BANNED); sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); return -1; } @@ -911,13 +894,9 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Check locked state for server AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); sLog->outDebug(LOG_FILTER_NETWORKIO, "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); - if (AccountTypes(security) < allowedAccountType) + if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) { - WorldPacket Packet (SMSG_AUTH_RESPONSE, 1); - Packet << uint8 (AUTH_UNAVAILABLE); - - SendPacket(packet); - + SendAuthResponseError(AUTH_UNAVAILABLE); sLog->outInfo(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); return -1; } @@ -926,29 +905,25 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) uint32 t = 0; uint32 seed = m_Seed; - sha.UpdateData (account); - sha.UpdateData ((uint8 *) & t, 4); - sha.UpdateData ((uint8 *) & clientSeed, 4); - sha.UpdateData ((uint8 *) & seed, 4); - sha.UpdateBigNumbers (&k, NULL); + sha.UpdateData(account); + sha.UpdateData((uint8*)&t, 4); + sha.UpdateData((uint8*)&clientSeed, 4); + sha.UpdateData((uint8*)&seed, 4); + sha.UpdateBigNumbers(&k, NULL); sha.Finalize(); std::string address = GetRemoteAddress(); - if (memcmp (sha.GetDigest(), digest, 20)) + if (memcmp(sha.GetDigest(), digest, 20)) { - packet.Initialize (SMSG_AUTH_RESPONSE, 1); - packet << uint8 (AUTH_FAILED); - - SendPacket(packet); - + SendAuthResponseError(AUTH_FAILED); sLog->outError(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); return -1; } sLog->outDebug(LOG_FILTER_NETWORKIO, "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", - account.c_str(), - address.c_str()); + account.c_str(), + address.c_str()); // Check if this user is by any chance a recruiter stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); @@ -971,7 +946,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) LoginDatabase.Execute(stmt); // NOTE ATM the socket is single-threaded, have this in mind ... - ACE_NEW_RETURN (m_Session, WorldSession (id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter), -1); + ACE_NEW_RETURN(m_Session, WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter), -1); m_Crypt.Init(&k); @@ -985,10 +960,9 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) // Sleep this Network thread for uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY); - ACE_OS::sleep (ACE_Time_Value (0, sleepTime)); - - sWorld->AddSession (m_Session); + ACE_OS::sleep(ACE_Time_Value(0, sleepTime)); + sWorld->AddSession(m_Session); return 0; } @@ -1049,7 +1023,14 @@ int WorldSocket::HandlePing (WorldPacket& recvPacket) } } - WorldPacket packet (SMSG_PONG, 4); + WorldPacket packet(SMSG_PONG, 4); packet << ping; return SendPacket(packet); } + +void WorldSocket::SendAuthResponseError(uint8 code) +{ + WorldPacket packet(SMSG_AUTH_RESPONSE, 1); + packet << uint8(code); + SendPacket(packet); +} diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 6b59647bb6c..2d5762ef60e 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -97,13 +97,13 @@ class WorldSocket : public WorldHandler typedef ACE_Guard<LockType> GuardType; /// Check if socket is closed. - bool IsClosed (void) const; + bool IsClosed(void) const; /// Close the socket. - void CloseSocket (void); + void CloseSocket(void); /// Get address of connected peer. - const std::string& GetRemoteAddress (void) const; + const std::string& GetRemoteAddress(void) const; /// Send A packet on the socket, this function is reentrant. /// @param pct packet to send @@ -111,57 +111,60 @@ class WorldSocket : public WorldHandler int SendPacket(const WorldPacket& pct); /// Add reference to this object. - long AddReference (void); + long AddReference(void); /// Remove reference to this object. - long RemoveReference (void); + long RemoveReference(void); /// things called by ACE framework. /// Called on open, the void* is the acceptor. - virtual int open (void *); + virtual int open(void *); /// Called on failures inside of the acceptor, don't call from your code. - virtual int close (u_long); + virtual int close(u_long); /// Called when we can read from the socket. - virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE); + virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); /// Called when the socket can write. - virtual int handle_output (ACE_HANDLE = ACE_INVALID_HANDLE); + virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE); /// Called when connection is closed or error happens. - virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE, + virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); /// Called by WorldSocketMgr/ReactorRunnable. - int Update (void); + int Update(void); private: /// Helper functions for processing incoming data. - int handle_input_header (void); - int handle_input_payload (void); - int handle_input_missing_data (void); + int handle_input_header(void); + int handle_input_payload(void); + int handle_input_missing_data(void); /// Help functions to mark/unmark the socket for output. /// @param g the guard is for m_OutBufferLock, the function will release it - int cancel_wakeup_output (GuardType& g); - int schedule_wakeup_output (GuardType& g); + int cancel_wakeup_output(GuardType& g); + int schedule_wakeup_output(GuardType& g); /// Drain the queue if its not empty. - int handle_output_queue (GuardType& g); + int handle_output_queue(GuardType& g); /// process one incoming packet. /// @param new_pct received packet, note that you need to delete it. - int ProcessIncoming (WorldPacket* new_pct); + int ProcessIncoming(WorldPacket* new_pct); /// Called by ProcessIncoming() on CMSG_AUTH_SESSION. - int HandleAuthSession (WorldPacket& recvPacket); + int HandleAuthSession(WorldPacket& recvPacket); /// Called by ProcessIncoming() on CMSG_PING. - int HandlePing (WorldPacket& recvPacket); + int HandlePing(WorldPacket& recvPacket); + + int HandleSendAuthSession(); private: + void SendAuthResponseError(uint8); /// Time in which the last ping was received ACE_Time_Value m_LastPingTime; diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 3b5c6131c5a..9230c46db15 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -449,8 +449,6 @@ int32 AuraEffect::CalculateAmount(Unit* caster) } } - float DoneActualBenefit = 0.0f; - // custom amount calculations go here switch (GetAuraType()) { @@ -483,278 +481,12 @@ int32 AuraEffect::CalculateAmount(Unit* caster) } break; case SPELL_AURA_SCHOOL_ABSORB: - m_canBeRecalculated = false; - if (!caster) - break; - switch (GetSpellInfo()->SpellFamilyName) - { - case SPELLFAMILY_MAGE: - // Ice Barrier - if (GetSpellInfo()->SpellFamilyFlags[1] & 0x1 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8) - { - // +80.68% from sp bonus - DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f; - // Glyph of Ice Barrier: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :) - // Glyph of Ice Barrier is only applied at the spell damage bonus because it was already applied to the base value in CalculateSpellDamage - DoneActualBenefit = caster->ApplyEffectModifiers(GetSpellInfo(), m_effIndex, DoneActualBenefit); - } - // Fire Ward - else if (GetSpellInfo()->SpellFamilyFlags[0] & 0x8 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8) - { - // +80.68% from sp bonus - DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f; - } - // Frost Ward - else if (GetSpellInfo()->SpellFamilyFlags[0] & 0x100 && GetSpellInfo()->SpellFamilyFlags[2] & 0x8) - { - // +80.68% from sp bonus - DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f; - } - break; - case SPELLFAMILY_WARLOCK: - // Shadow Ward - if (m_spellInfo->SpellFamilyFlags[2] & 0x40) - { - // +80.68% from sp bonus - DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8068f; - } - break; - case SPELLFAMILY_PRIEST: - // Power Word: Shield - if (GetSpellInfo()->SpellFamilyFlags[0] & 0x1 && GetSpellInfo()->SpellFamilyFlags[2] & 0x400) - { - // +80.68% from sp bonus - float bonus = 0.8068f; - - // Borrowed Time - if (AuraEffect const* pAurEff = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 2899, 1)) - bonus += CalculatePct(1.0f, pAurEff->GetAmount()); - - DoneActualBenefit += caster->SpellBaseHealingBonusDone(m_spellInfo->GetSchoolMask()) * bonus; - // Improved PW: Shield: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :) - // Improved PW: Shield is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage - DoneActualBenefit = caster->ApplyEffectModifiers(GetSpellInfo(), m_effIndex, DoneActualBenefit); - DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo()); - - amount += int32(DoneActualBenefit); - - // Twin Disciplines - if (AuraEffect const* pAurEff = caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PRIEST, 0x400000, 0, 0, caster->GetGUID())) - AddPct(amount, pAurEff->GetAmount()); - - // Focused Power - // Reuse variable, not sure if this code below can be moved before Twin Disciplines - DoneActualBenefit = float(amount); - DoneActualBenefit *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT); - amount = int32(DoneActualBenefit); - - return amount; - } - break; - case SPELLFAMILY_PALADIN: - // Sacred Shield - if (m_spellInfo->SpellFamilyFlags[1] & 0x80000) - { - //+75.00% from sp bonus - float bonus = 0.75f; - - DoneActualBenefit += caster->SpellBaseHealingBonusDone(m_spellInfo->GetSchoolMask()) * bonus; - // Divine Guardian is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage - DoneActualBenefit = caster->ApplyEffectModifiers(GetSpellInfo(), m_effIndex, DoneActualBenefit); - DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo()); - - amount += (int32)DoneActualBenefit; - - // Arena - Dampening - AuraEffect const* pAurEff = caster->GetAuraEffect(74410, 0); - if (!pAurEff) - pAurEff = caster->GetAuraEffect(74411, 0); // Battleground - Dampening - if (pAurEff) - AddPct(amount, pAurEff->GetAmount()); - - return amount; - } - break; - default: - break; - } - break; case SPELL_AURA_MANA_SHIELD: m_canBeRecalculated = false; - if (!caster) - break; - // Mana Shield - if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_MAGE && GetSpellInfo()->SpellFamilyFlags[0] & 0x8000 && m_spellInfo->SpellFamilyFlags[2] & 0x8) - { - // +80.53% from +spd bonus - DoneActualBenefit += caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask()) * 0.8053f; - } - break; - case SPELL_AURA_DUMMY: - if (!caster) - break; - // Earth Shield - if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[1] & 0x400) - { - amount = caster->SpellHealingBonusDone(GetBase()->GetUnitOwner(), GetSpellInfo(), amount, SPELL_DIRECT_DAMAGE); - amount = GetBase()->GetUnitOwner()->SpellHealingBonusTaken(caster, GetSpellInfo(), amount, SPELL_DIRECT_DAMAGE); - } - break; - case SPELL_AURA_PERIODIC_DAMAGE: - if (!caster) - break; - // Rupture - if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_ROGUE && m_spellInfo->SpellFamilyFlags[0] & 0x100000) - { - m_canBeRecalculated = false; - if (caster->GetTypeId() != TYPEID_PLAYER) - break; - //1 point : ${($m1+$b1*1+0.015*$AP)*4} damage over 8 secs - //2 points: ${($m1+$b1*2+0.024*$AP)*5} damage over 10 secs - //3 points: ${($m1+$b1*3+0.03*$AP)*6} damage over 12 secs - //4 points: ${($m1+$b1*4+0.03428571*$AP)*7} damage over 14 secs - //5 points: ${($m1+$b1*5+0.0375*$AP)*8} damage over 16 secs - float AP_per_combo[6] = {0.0f, 0.015f, 0.024f, 0.03f, 0.03428571f, 0.0375f}; - uint8 cp = caster->ToPlayer()->GetComboPoints(); - if (cp > 5) cp = 5; - amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * AP_per_combo[cp]); - } - // Rip - else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[0] & 0x00800000 && GetAuraType() == SPELL_AURA_PERIODIC_DAMAGE) - { - m_canBeRecalculated = false; - // 0.01*$AP*cp - if (caster->GetTypeId() != TYPEID_PLAYER) - break; - - uint8 cp = caster->ToPlayer()->GetComboPoints(); - - // Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs - if (AuraEffect const* aurEff = caster->GetAuraEffect(34241, EFFECT_0)) - amount += cp * aurEff->GetAmount(); - // Idol of Worship. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs - else if (AuraEffect const* aurEff = caster->GetAuraEffect(60774, EFFECT_0)) - amount += cp * aurEff->GetAmount(); - - amount += uint32(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), cp)); - } - // Rend - else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_WARRIOR && GetSpellInfo()->SpellFamilyFlags[0] & 0x20) - { - m_canBeRecalculated = false; - // $0.2 * (($MWB + $mwb) / 2 + $AP / 14 * $MWS) bonus per tick - float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); - int32 mws = caster->GetAttackTime(BASE_ATTACK); - float mwb_min = caster->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE); - float mwb_max = caster->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE); - float mwb = ((mwb_min + mwb_max) / 2 + ap * mws / 14000) * 0.2f; - amount += int32(caster->ApplyEffectModifiers(m_spellInfo, m_effIndex, mwb)); - // "If used while your target is above 75% health, Rend does 35% more damage." - // as for 3.1.3 only ranks above 9 (wrong tooltip?) - if (m_spellInfo->GetRank() >= 9) - { - if (GetBase()->GetUnitOwner()->HasAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, m_spellInfo, caster)) - AddPct(amount, m_spellInfo->Effects[EFFECT_2].CalcValue(caster)); - } - } - break; - case SPELL_AURA_PERIODIC_ENERGIZE: - switch (m_spellInfo->Id) - { - case 29166: // Innervate - ApplyPct(amount, float(GetBase()->GetUnitOwner()->GetCreatePowers(POWER_MANA)) / GetTotalTicks()); - break; - case 48391: // Owlkin Frenzy - ApplyPct(amount, GetBase()->GetUnitOwner()->GetCreatePowers(POWER_MANA)); - break; - default: - break; - } - break; - case SPELL_AURA_PERIODIC_HEAL: - if (!caster) - break; - // Lightwell Renew - if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags[2] & 0x4000) - { - if (caster->GetTypeId() == TYPEID_PLAYER) - // Bonus from Glyph of Lightwell - if (AuraEffect* modHealing = caster->GetAuraEffect(55673, 0)) - AddPct(amount, modHealing->GetAmount()); - } - break; - case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: - if (!caster) - break; - // Icebound Fortitude - if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && m_spellInfo->SpellFamilyFlags[0] & 0x00100000) - { - if (caster->GetTypeId() == TYPEID_PLAYER) - { - int32 value = (-1 * amount) - 10; - uint32 defva = uint32(caster->ToPlayer()->GetSkillValue(SKILL_DEFENSE) + caster->ToPlayer()->GetRatingBonusValue(CR_DEFENSE_SKILL)); - - if (defva > 400) - value += int32((defva - 400) * 0.15); - - // Glyph of Icebound Fortitude - if (AuraEffect const* aurEff = caster->GetAuraEffect(58625, 0)) - { - int32 valMax = aurEff->GetAmount(); - if (value < valMax) - value = valMax; - } - amount = -value; - } - } - // Hand of Salvation - else if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PALADIN && GetSpellInfo()->SpellFamilyFlags[0] & 0x00000100) - { - //Glyph of Salvation - if (caster->GetGUID() == GetBase()->GetUnitOwner()->GetGUID()) - if (AuraEffect const* aurEff = caster->GetAuraEffect(63225, 0)) - amount = -aurEff->GetAmount(); - } - break; - case SPELL_AURA_MOD_THREAT: - { - uint8 level_diff = 0; - float multiplier = 0.0f; - switch (GetId()) - { - // Arcane Shroud - case 26400: - level_diff = GetBase()->GetUnitOwner()->getLevel() - 60; - multiplier = 2; - break; - // The Eye of Diminution - case 28862: - level_diff = GetBase()->GetUnitOwner()->getLevel() - 60; - multiplier = 1; - break; - } - if (level_diff > 0) - amount += int32(multiplier * level_diff); - break; - } - case SPELL_AURA_MOD_INCREASE_HEALTH: - // Vampiric Blood - if (GetId() == 55233) - amount = GetBase()->GetUnitOwner()->CountPctFromMaxHealth(amount); - break; - case SPELL_AURA_MOD_INCREASE_SPEED: - // Dash - do not set speed if not in cat form - if (GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID && GetSpellInfo()->SpellFamilyFlags[2] & 0x00000008) - amount = GetBase()->GetUnitOwner()->GetShapeshiftForm() == FORM_CAT ? amount : 0; break; default: break; } - if (DoneActualBenefit != 0.0f) - { - DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellInfo()); - amount += (int32)DoneActualBenefit; - } GetBase()->CallScriptEffectCalcAmountHandlers(const_cast<AuraEffect const*>(this), amount, m_canBeRecalculated); amount *= GetBase()->GetStackAmount(); @@ -848,32 +580,6 @@ void AuraEffect::CalculateSpellMod() { switch (GetAuraType()) { - case SPELL_AURA_DUMMY: - switch (GetSpellInfo()->SpellFamilyName) - { - case SPELLFAMILY_DRUID: - switch (GetId()) - { - case 34246: // Idol of the Emerald Queen - case 60779: // Idol of Lush Moss - { - if (!m_spellmod) - { - m_spellmod = new SpellModifier(GetBase()); - m_spellmod->op = SPELLMOD_DOT; - m_spellmod->type = SPELLMOD_FLAT; - m_spellmod->spellId = GetId(); - m_spellmod->mask[1] = 0x0010; - } - m_spellmod->value = GetAmount()/7; - } - break; - } - break; - default: - break; - } - break; case SPELL_AURA_ADD_FLAT_MODIFIER: case SPELL_AURA_ADD_PCT_MODIFIER: if (!m_spellmod) @@ -882,7 +588,7 @@ void AuraEffect::CalculateSpellMod() m_spellmod->op = SpellModOp(GetMiscValue()); ASSERT(m_spellmod->op < MAX_SPELLMOD); - m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types + m_spellmod->type = SpellModType(GetAuraType()); // SpellModType value == spell aura types m_spellmod->spellId = GetId(); m_spellmod->mask = GetSpellInfo()->Effects[GetEffIndex()].SpellClassMask; m_spellmod->charges = GetBase()->GetCharges(); @@ -1274,7 +980,10 @@ void AuraEffect::PeriodicTick(AuraApplication * aurApp, Unit* caster) const void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) { - // TODO: effect script handlers here + bool prevented = GetBase()->CallScriptEffectProcHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), eventInfo); + if (prevented) + return; + switch (GetAuraType()) { case SPELL_AURA_PROC_TRIGGER_SPELL: @@ -1295,6 +1004,8 @@ void AuraEffect::HandleProc(AuraApplication* aurApp, ProcEventInfo& eventInfo) default: break; } + + GetBase()->CallScriptAfterEffectProcHandlers(const_cast<AuraEffect const*>(this), const_cast<AuraApplication const*>(aurApp), eventInfo); } void AuraEffect::CleanupTriggeredSpells(Unit* target) @@ -1854,7 +1565,7 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app // call functions which may have additional effects after chainging state of unit // phase auras normally not expected at BG but anyway better check - if (apply && (mode & AURA_EFFECT_HANDLE_REAL)) + if (apply) { // drop flag at invisibiliy in bg target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); @@ -4973,10 +4684,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool if (Aura* newAura = target->AddAura(71564, target)) newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount); break; - case 59628: // Tricks of the Trade - if (caster && caster->GetMisdirectionTarget()) - target->SetReducedThreatPercent(100, caster->GetMisdirectionTarget()->GetGUID()); - break; } } // AT REMOVE @@ -5072,20 +4779,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool if (GetId() == 61777) target->CastSpell(target, GetAmount(), true); break; - case SPELLFAMILY_ROGUE: - // Tricks of the trade - switch (GetId()) - { - case 59628: //Tricks of the trade buff on rogue (6sec duration) - target->SetReducedThreatPercent(0, 0); - break; - case 57934: //Tricks of the trade buff on rogue (30sec duration) - if (aurApp->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE || !caster->GetMisdirectionTarget()) - target->SetReducedThreatPercent(0, 0); - else - target->SetReducedThreatPercent(0, caster->GetMisdirectionTarget()->GetGUID()); - break; - } default: break; } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 24a83c7990c..795908ce4b5 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1537,17 +1537,6 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b // mods at aura apply or remove switch (GetSpellInfo()->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - switch (GetId()) - { - case 50720: // Vigilance - if (apply) - target->CastSpell(caster, 59665, true, 0, 0, caster->GetGUID()); - else - target->SetReducedThreatPercent(0, 0); - break; - } - break; case SPELLFAMILY_DRUID: // Enrage if ((GetSpellInfo()->SpellFamilyFlags[0] & 0x80000) && GetSpellInfo()->SpellIconID == 961) @@ -1940,9 +1929,12 @@ void Aura::AddProcCooldown(uint32 /*msec*/) //m_procCooldown = time(NULL) + msec; } -void Aura::PrepareProcToTrigger() +void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo) { - // TODO: allow scripts to prevent charge drop/cooldown + bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo); + if (!prepare) + return; + // take one charge, aura expiration will be handled in Aura::TriggerProcOnEvent (if needed) if (IsUsingCharges()) { @@ -1981,14 +1973,18 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI return false; // do checks using conditions table - ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, GetSpellInfo()->Id); + ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_PROC, GetId()); ConditionSourceInfo condInfo = ConditionSourceInfo(eventInfo.GetActor(), eventInfo.GetActionTarget()); if (!sConditionMgr->IsObjectMeetToConditions(condInfo, conditions)) return false; + // AuraScript Hook + bool check = const_cast<Aura*>(this)->CallScriptCheckProcHandlers(aurApp, eventInfo); + if (!check) + return false; + // TODO: - // - add DoCheckProc() AuraScript hook - // to allow additional requirements for procs + // do allow additional requirements for procs // this is needed because this is the last moment in which you can prevent aura charge drop on proc // and possibly a way to prevent default checks (if there're going to be any) @@ -2052,14 +2048,14 @@ float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& event void Aura::TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) { - // TODO: OnProc hook here + CallScriptProcHandlers(const_cast<AuraApplication const*>(aurApp), eventInfo); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (aurApp->HasEffect(i)) - // TODO: OnEffectProc hook here (allowing prevention of selected effects) + // OnEffectProc / AfterEffectProc hooks handled in AuraEffect::HandleProc() GetEffect(i)->HandleProc(aurApp, eventInfo); - // TODO: AfterEffectProc hook here - // TODO: AfterProc hook here + CallScriptAfterProcHandlers(const_cast<AuraApplication const*>(aurApp), eventInfo); // Remove aura if we've used last charge to proc if (IsUsingCharges() && !GetCharges()) @@ -2355,6 +2351,95 @@ void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication con } } +bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_PROC, aurApp); + std::list<AuraScript::CheckProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckProc.end(), hookItr = (*scritr)->DoCheckProc.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + if (!(*hookItr).Call(*scritr, eventInfo)) + return false; + (*scritr)->_FinishScriptCall(); + } + return true; +} + +bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + bool prepare = true; + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PREPARE_PROC, aurApp); + std::list<AuraScript::AuraProcHandler>::iterator effEndItr = (*scritr)->DoPrepareProc.end(), effItr = (*scritr)->DoPrepareProc.begin(); + for (; effItr != effEndItr; ++effItr) + (*effItr).Call(*scritr, eventInfo); + + if (prepare && (*scritr)->_IsDefaultActionPrevented()) + prepare = false; + (*scritr)->_FinishScriptCall(); + } + return prepare; +} + +void Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PROC, aurApp); + std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->OnProc.end(), hookItr = (*scritr)->OnProc.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + (*hookItr).Call(*scritr, eventInfo); + (*scritr)->_FinishScriptCall(); + } +} + +void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_PROC, aurApp); + std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->AfterProc.end(), hookItr = (*scritr)->AfterProc.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + (*hookItr).Call(*scritr, eventInfo); + (*scritr)->_FinishScriptCall(); + } +} + +bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + bool preventDefault = false; + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_PROC, aurApp); + std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->OnEffectProc.end(), effItr = (*scritr)->OnEffectProc.begin(); + for (; effItr != effEndItr; ++effItr) + { + if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + (*effItr).Call(*scritr, aurEff, eventInfo); + } + if (!preventDefault) + preventDefault = (*scritr)->_IsDefaultActionPrevented(); + (*scritr)->_FinishScriptCall(); + } + return preventDefault; +} + +void Aura::CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo) +{ + for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC, aurApp); + std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->AfterEffectProc.end(), effItr = (*scritr)->AfterEffectProc.begin(); + for (; effItr != effEndItr; ++effItr) + { + if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + (*effItr).Call(*scritr, aurEff, eventInfo); + } + (*scritr)->_FinishScriptCall(); + } +} + UnitAura::UnitAura(SpellInfo const* spellproto, uint8 effMask, WorldObject* owner, Unit* caster, int32 *baseAmount, Item* castItem, uint64 casterGUID) : Aura(spellproto, owner, caster, castItem, casterGUID) { diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 88ebee18981..bd351548255 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -194,7 +194,7 @@ class Aura void AddProcCooldown(uint32 msec); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } - void PrepareProcToTrigger(); + void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo); bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); @@ -218,6 +218,13 @@ class Aura void CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool & defaultPrevented); void CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount); void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount); + // Spell Proc Hooks + bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + bool CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + void CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + void CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); + bool CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); + void CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); std::list<AuraScript*> m_loadedScripts; private: diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 9bb3eaa31f1..4d75aec2cae 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -490,7 +490,7 @@ SpellValue::SpellValue(SpellInfo const* proto) Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags, uint64 originalCasterGUID, bool skipCheck) : m_spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(info, caster)), m_caster((info->AttributesEx6 & SPELL_ATTR6_CAST_BY_CHARMER && caster->GetCharmerOrOwner()) ? caster->GetCharmerOrOwner() : caster) -, m_spellValue(new SpellValue(m_spellInfo)) +, m_spellValue(new SpellValue(m_spellInfo)), m_preGeneratedPath(PathGenerator(m_caster)) { m_customError = SPELL_CUSTOM_ERROR_NONE; m_skipCheck = skipCheck; @@ -610,6 +610,7 @@ Spell::~Spell() if (m_caster && m_caster->GetTypeId() == TYPEID_PLAYER) ASSERT(m_caster->ToPlayer()->m_spellModTakingSpell != this); + delete m_spellValue; CheckEffectExecuteData(); @@ -1345,11 +1346,6 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge } } - // todo: move to scripts, but we must call it before resize list by MaxAffectedTargets - // Intimidating Shout - if (m_spellInfo->Id == 5246 && effIndex != EFFECT_0) - unitTargets.remove(m_targets.GetUnitTarget()); - // Other special target selection goes here if (uint32 maxTargets = m_spellValue->MaxAffectedTargets) { @@ -5148,12 +5144,30 @@ SpellCastResult Spell::CheckCast(bool strict) if (strict && m_caster->IsScriptOverriden(m_spellInfo, 6953)) m_caster->RemoveMovementImpairingAuras(); } + if (m_caster->HasUnitState(UNIT_STATE_ROOT)) return SPELL_FAILED_ROOTED; + + Unit* target = m_targets.GetUnitTarget(); + + if (!target) + return SPELL_FAILED_DONT_REPORT; + if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Unit* target = m_targets.GetUnitTarget()) - if (!target->isAlive()) - return SPELL_FAILED_BAD_TARGETS; + if (!target->isAlive()) + return SPELL_FAILED_BAD_TARGETS; + + Position pos; + target->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); + target->GetFirstCollisionPosition(pos, CONTACT_DISTANCE, target->GetRelativeAngle(m_caster)); + + m_preGeneratedPath.SetPathLengthLimit(m_spellInfo->GetMaxRange(true) * 1.5f); + bool result = m_preGeneratedPath.CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ + target->GetObjectSize()); + if (m_preGeneratedPath.GetPathType() & PATHFIND_SHORT) + return SPELL_FAILED_OUT_OF_RANGE; + else if (!result) + return SPELL_FAILED_NOPATH; + break; } case SPELL_EFFECT_SKINNING: diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 1fe8affc407..903d4487c31 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -23,6 +23,7 @@ #include "SharedDefines.h" #include "ObjectMgr.h" #include "SpellInfo.h" +#include "PathGenerator.h" class Unit; class Player; @@ -665,6 +666,7 @@ class Spell bool m_skipCheck; uint8 m_auraScaleMask; + PathGenerator m_preGeneratedPath; ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS]; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 1dddb08be0b..ba7c87ba10a 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -63,6 +63,7 @@ #include "GameObjectAI.h" #include "AccountMgr.h" #include "InstanceScript.h" +#include "PathGenerator.h" #include "ReputationMgr.h" pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= @@ -883,12 +884,6 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) m_caster->CastSpell(unitTarget, spell->Id, true); return; } - // Righteous Defense - case 31980: - { - m_caster->CastSpell(unitTarget, 31790, true); - return; - } // Cloak of Shadows case 35729: { @@ -1139,32 +1134,8 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) return; // Pre effects - uint8 uiMaxSafeLevel = 0; switch (m_spellInfo->Id) { - case 48129: // Scroll of Recall - uiMaxSafeLevel = 40; - case 60320: // Scroll of Recall II - if (!uiMaxSafeLevel) - uiMaxSafeLevel = 70; - case 60321: // Scroll of Recal III - if (!uiMaxSafeLevel) - uiMaxSafeLevel = 80; - - if (unitTarget->getLevel() > uiMaxSafeLevel) - { - unitTarget->AddAura(60444, unitTarget); //Apply Lost! Aura - - // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335 - uint32 spellId = 60323; - if (m_caster->ToPlayer()->GetTeam() == HORDE) - spellId += 5; - - spellId += urand(0, 7); - m_caster->CastSpell(m_caster, spellId, true); - return; - } - break; case 66550: // teleports outside (Isle of Conquest) if (Player* target = unitTarget->ToPlayer()) { @@ -1188,7 +1159,7 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) // If not exist data for dest location - return if (!m_targets.HasDst()) { - sLog->outError(LOG_FILTER_SPELLS_AURAS, "Spell::EffectTeleportUnits - does not have destination for spell ID %u\n", m_spellInfo->Id); + sLog->outError(LOG_FILTER_SPELLS_AURAS, "Spell::EffectTeleportUnits - does not have destination for spellId %u.", m_spellInfo->Id); return; } @@ -1585,10 +1556,6 @@ void Spell::EffectHealPct(SpellEffIndex /*effIndex*/) if (!m_originalCaster) return; - // Rune Tap - Party - if (m_spellInfo->Id == 59754 && unitTarget == m_caster) - return; - uint32 heal = m_originalCaster->SpellHealingBonusDone(unitTarget, m_spellInfo, unitTarget->CountPctFromMaxHealth(damage), HEAL); heal = unitTarget->SpellHealingBonusTaken(m_originalCaster, m_spellInfo, heal, HEAL); @@ -3732,23 +3699,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) return; unitTarget->RemoveAurasDueToSpell(m_spellInfo->Effects[effIndex].CalcValue()); break; - // PX-238 Winter Wondervolt TRAP - case 26275: - { - uint32 spells[4] = { 26272, 26157, 26273, 26274 }; - - // check presence - for (uint8 j = 0; j < 4; ++j) - if (unitTarget->HasAuraEffect(spells[j], 0)) - return; - - // select spell - uint32 iTmpSpellId = spells[urand(0, 3)]; - - // cast - unitTarget->CastSpell(unitTarget, iTmpSpellId, true); - return; - } // Bending Shinbone case 8856: { @@ -3794,14 +3744,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) m_caster->CastSpell(unitTarget, 22682, true); return; } - // Piccolo of the Flaming Fire - case 17512: - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - unitTarget->HandleEmoteCommand(EMOTE_STATE_DANCE); - return; - } // Decimate case 28374: case 54426: @@ -3944,17 +3886,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) m_caster->MonsterTextEmote(buf, 0); break; } - // Vigilance - case 50725: - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) - return; - - // Remove Taunt cooldown - unitTarget->ToPlayer()->RemoveSpellCooldown(355, true); - - return; - } // Death Knight Initiate Visual case 51519: { @@ -4113,17 +4044,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) } return; } - case 63845: // Create Lance - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - if (m_caster->ToPlayer()->GetTeam() == ALLIANCE) - m_caster->CastSpell(m_caster, 63914, true); - else - m_caster->CastSpell(m_caster, 63919, true); - return; - } case 59317: // Teleporting { @@ -4317,46 +4237,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) return; } } - case SPELLFAMILY_POTION: - { - switch (m_spellInfo->Id) - { - // Netherbloom - case 28702: - { - if (!unitTarget) - return; - // 25% chance of casting a random buff - if (roll_chance_i(75)) - return; - - // triggered spells are 28703 to 28707 - // Note: some sources say, that there was the possibility of - // receiving a debuff. However, this seems to be removed by a patch. - const uint32 spellid = 28703; - - // don't overwrite an existing aura - for (uint8 i = 0; i < 5; ++i) - if (unitTarget->HasAura(spellid + i)) - return; - unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true); - break; - } - - // Nightmare Vine - case 28720: - { - if (!unitTarget) - return; - // 25% chance of casting Nightmare Pollen - if (roll_chance_i(75)) - return; - unitTarget->CastSpell(unitTarget, 28721, true); - break; - } - } - break; - } case SPELLFAMILY_DEATHKNIGHT: { // Pestilence @@ -4377,19 +4257,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) } break; } - case SPELLFAMILY_WARRIOR: - { - // Shattering Throw - if (m_spellInfo->SpellFamilyFlags[1] & 0x00400000) - { - if (!unitTarget) - return; - // remove shields, will still display immune to damage part - unitTarget->RemoveAurasWithMechanic(1<<MECHANIC_IMMUNE_SHIELD, AURA_REMOVE_BY_ENEMY_SPELL); - return; - } - break; - } } // normal DB scripted effect @@ -4868,45 +4735,12 @@ void Spell::EffectResurrect(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget) - return; - if (unitTarget->GetTypeId() != TYPEID_PLAYER) + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - if (unitTarget->isAlive()) - return; - if (!unitTarget->IsInWorld()) + if (unitTarget->isAlive() || !unitTarget->IsInWorld()) return; - switch (m_spellInfo->Id) - { - // Defibrillate (Goblin Jumper Cables) have 33% chance on success - case 8342: - if (roll_chance_i(67)) - { - m_caster->CastSpell(m_caster, 8338, true, m_CastItem); - return; - } - break; - // Defibrillate (Goblin Jumper Cables XL) have 50% chance on success - case 22999: - if (roll_chance_i(50)) - { - m_caster->CastSpell(m_caster, 23055, true, m_CastItem); - return; - } - break; - // Defibrillate (Gnomish Army Knife) have 67% chance on success_list - case 54732: - if (roll_chance_i(33)) - { - return; - } - break; - default: - break; - } - Player* target = unitTarget->ToPlayer(); if (target->isRessurectRequested()) // already have one active request @@ -5097,25 +4931,24 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/) void Spell::EffectCharge(SpellEffIndex /*effIndex*/) { + if (!unitTarget) + return; + if (effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET) { - if (!unitTarget) - return; - - float angle = unitTarget->GetRelativeAngle(m_caster); - Position pos; - - unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); - unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), angle); - - m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ + unitTarget->GetObjectSize()); + if (m_preGeneratedPath.GetPathType() & PATHFIND_NOPATH) + { + Position pos; + unitTarget->GetContactPoint(m_caster, pos.m_positionX, pos.m_positionY, pos.m_positionZ); + unitTarget->GetFirstCollisionPosition(pos, unitTarget->GetObjectSize(), unitTarget->GetRelativeAngle(m_caster)); + m_caster->GetMotionMaster()->MoveCharge(pos.m_positionX, pos.m_positionY, pos.m_positionZ); + } + else + m_caster->GetMotionMaster()->MoveCharge(m_preGeneratedPath); } if (effectHandleMode == SPELL_EFFECT_HANDLE_HIT_TARGET) { - if (!unitTarget) - return; - // not all charge effects used in negative spells if (!m_spellInfo->IsPositive() && m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->Attack(unitTarget, true); @@ -5151,26 +4984,10 @@ void Spell::EffectKnockBack(SpellEffIndex effIndex) if (creatureTarget->isWorldBoss() || creatureTarget->IsDungeonBoss()) return; - // Spells with SPELL_EFFECT_KNOCK_BACK(like Thunderstorm) can't knoback target if target has ROOT/STUN + // Spells with SPELL_EFFECT_KNOCK_BACK (like Thunderstorm) can't knockback target if target has ROOT/STUN if (unitTarget->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) return; - // Typhoon - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x01000000) - { - // Glyph of Typhoon - if (m_caster->HasAura(62135)) - return; - } - - // Thunderstorm - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[1] & 0x00002000) - { - // Glyph of Thunderstorm - if (m_caster->HasAura(62132)) - return; - } - // Instantly interrupt non melee spells being casted if (unitTarget->IsNonMeleeSpellCasted(true)) unitTarget->InterruptNonMeleeSpells(true); @@ -5974,7 +5791,7 @@ void Spell::EffectRedirectThreat(SpellEffIndex /*effIndex*/) return; if (unitTarget) - m_caster->SetReducedThreatPercent((uint32)damage, unitTarget->GetGUID()); + m_caster->SetRedirectThreat(unitTarget->GetGUID(), uint32(damage)); } void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 582f13284bb..b258f01ccd5 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3633,6 +3633,8 @@ void SpellMgr::LoadDbcDataCorrections() case 49345: // Call Emerald Drake spellInfo->Effect[1] = 0; break; + case 24314: // Threatening Gaze + spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_JUMP; default: break; } diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index c29b08242a0..89ed223545f 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -470,7 +470,7 @@ WorldLocation* SpellScript::GetHitDest() { if (!IsInEffectHook()) { - sLog->outError(LOG_FILTER_TSCR, "Script: `%s` Spell: `%u`: function SpellScript::GetHitGObj was called, but function has no effect in current hook!", m_scriptName->c_str(), m_scriptSpellId); + sLog->outError(LOG_FILTER_TSCR, "Script: `%s` Spell: `%u`: function SpellScript::GetHitDest was called, but function has no effect in current hook!", m_scriptName->c_str(), m_scriptSpellId); return NULL; } return m_spell->destTarget; @@ -679,6 +679,30 @@ bool AuraScript::_Validate(SpellInfo const* entry) if (!(*itr).GetAffectedEffectsMask(entry)) sLog->outError(LOG_FILTER_TSCR, "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectSplit` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); + for (std::list<CheckProcHandler>::iterator itr = DoCheckProc.begin(); itr != DoCheckProc.end(); ++itr) + if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) + sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoCheckProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + + for (std::list<AuraProcHandler>::iterator itr = DoPrepareProc.begin(); itr != DoPrepareProc.end(); ++itr) + if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) + sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `DoPrepareProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + + for (std::list<AuraProcHandler>::iterator itr = OnProc.begin(); itr != OnProc.end(); ++itr) + if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) + sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `OnProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + + for (std::list<AuraProcHandler>::iterator itr = AfterProc.begin(); itr != AfterProc.end(); ++itr) + if (!entry->HasEffect(SPELL_EFFECT_APPLY_AURA) && !entry->HasAreaAuraEffect()) + sLog->outError(LOG_FILTER_TSCR, "Spell `%u` of script `%s` does not have apply aura effect - handler bound to hook `AfterProc` of AuraScript won't be executed", entry->Id, m_scriptName->c_str()); + + for (std::list<EffectProcHandler>::iterator itr = OnEffectProc.begin(); itr != OnEffectProc.end(); ++itr) + if (!(*itr).GetAffectedEffectsMask(entry)) + sLog->outError(LOG_FILTER_TSCR, "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `OnEffectProc` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); + + for (std::list<EffectProcHandler>::iterator itr = AfterEffectProc.begin(); itr != AfterEffectProc.end(); ++itr) + if (!(*itr).GetAffectedEffectsMask(entry)) + sLog->outError(LOG_FILTER_TSCR, "Spell `%u` Effect `%s` of script `%s` did not match dbc effect data - handler bound to hook `AfterEffectProc` of AuraScript won't be executed", entry->Id, (*itr).ToString().c_str(), m_scriptName->c_str()); + return _SpellScript::_Validate(entry); } @@ -818,6 +842,37 @@ void AuraScript::EffectSplitHandler::Call(AuraScript* auraScript, AuraEffect* au (auraScript->*pEffectHandlerScript)(aurEff, dmgInfo, splitAmount); } +AuraScript::CheckProcHandler::CheckProcHandler(AuraCheckProcFnType handlerScript) +{ + _HandlerScript = handlerScript; +} + +bool AuraScript::CheckProcHandler::Call(AuraScript* auraScript, ProcEventInfo& eventInfo) +{ + return (auraScript->*_HandlerScript)(eventInfo); +} + +AuraScript::AuraProcHandler::AuraProcHandler(AuraProcFnType handlerScript) +{ + _HandlerScript = handlerScript; +} + +void AuraScript::AuraProcHandler::Call(AuraScript* auraScript, ProcEventInfo& eventInfo) +{ + (auraScript->*_HandlerScript)(eventInfo); +} + +AuraScript::EffectProcHandler::EffectProcHandler(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) + : AuraScript::EffectBase(effIndex, effName) +{ + _EffectHandlerScript = effectHandlerScript; +} + +void AuraScript::EffectProcHandler::Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo) +{ + (auraScript->*_EffectHandlerScript)(aurEff, eventInfo); +} + bool AuraScript::_Load(Aura* aura) { m_aura = aura; @@ -853,6 +908,8 @@ bool AuraScript::_IsDefaultActionPrevented() case AURA_SCRIPT_HOOK_EFFECT_PERIODIC: case AURA_SCRIPT_HOOK_EFFECT_ABSORB: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: + case AURA_SCRIPT_HOOK_PREPARE_PROC: + case AURA_SCRIPT_HOOK_EFFECT_PROC: return m_defaultActionPrevented; default: ASSERT(false && "AuraScript::_IsDefaultActionPrevented is called in a wrong place"); @@ -869,6 +926,8 @@ void AuraScript::PreventDefaultAction() case AURA_SCRIPT_HOOK_EFFECT_PERIODIC: case AURA_SCRIPT_HOOK_EFFECT_ABSORB: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: + case AURA_SCRIPT_HOOK_PREPARE_PROC: + case AURA_SCRIPT_HOOK_EFFECT_PROC: m_defaultActionPrevented = true; break; default: @@ -1051,6 +1110,12 @@ Unit* AuraScript::GetTarget() const case AURA_SCRIPT_HOOK_EFFECT_MANASHIELD: case AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD: case AURA_SCRIPT_HOOK_EFFECT_SPLIT: + case AURA_SCRIPT_HOOK_CHECK_PROC: + case AURA_SCRIPT_HOOK_PREPARE_PROC: + case AURA_SCRIPT_HOOK_PROC: + case AURA_SCRIPT_HOOK_AFTER_PROC: + case AURA_SCRIPT_HOOK_EFFECT_PROC: + case AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC: return m_auraApplication->GetTarget(); default: sLog->outError(LOG_FILTER_TSCR, "Script: `%s` Spell: `%u` AuraScript::GetTarget called in a hook in which the call won't have effect!", m_scriptName->c_str(), m_scriptSpellId); diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 5791c701c07..6f1df2560dc 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -428,14 +428,22 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_EFFECT_SPLIT, AURA_SCRIPT_HOOK_CHECK_AREA_TARGET, AURA_SCRIPT_HOOK_DISPEL, - AURA_SCRIPT_HOOK_AFTER_DISPEL + AURA_SCRIPT_HOOK_AFTER_DISPEL, + // Spell Proc Hooks + AURA_SCRIPT_HOOK_CHECK_PROC, + AURA_SCRIPT_HOOK_PREPARE_PROC, + AURA_SCRIPT_HOOK_PROC, + AURA_SCRIPT_HOOK_EFFECT_PROC, + AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC, + AURA_SCRIPT_HOOK_AFTER_PROC, /*AURA_SCRIPT_HOOK_APPLY, AURA_SCRIPT_HOOK_REMOVE, */ }; +/* #define HOOK_AURA_EFFECT_START HOOK_AURA_EFFECT_APPLY #define HOOK_AURA_EFFECT_END HOOK_AURA_EFFECT_CALC_SPELLMOD + 1 #define HOOK_AURA_EFFECT_COUNT HOOK_AURA_EFFECT_END - HOOK_AURA_EFFECT_START - +*/ class AuraScript : public _SpellScript { // internal use classes & functions @@ -453,6 +461,9 @@ class AuraScript : public _SpellScript typedef void(CLASSNAME::*AuraEffectCalcSpellModFnType)(AuraEffect const*, SpellModifier* &); \ typedef void(CLASSNAME::*AuraEffectAbsorbFnType)(AuraEffect*, DamageInfo &, uint32 &); \ typedef void(CLASSNAME::*AuraEffectSplitFnType)(AuraEffect*, DamageInfo &, uint32 &); \ + typedef bool(CLASSNAME::*AuraCheckProcFnType)(ProcEventInfo&); \ + typedef void(CLASSNAME::*AuraProcFnType)(ProcEventInfo&); \ + typedef void(CLASSNAME::*AuraEffectProcFnType)(AuraEffect const*, ProcEventInfo&); \ AURASCRIPT_FUNCTION_TYPE_DEFINES(AuraScript) @@ -552,6 +563,30 @@ class AuraScript : public _SpellScript private: AuraEffectSplitFnType pEffectHandlerScript; }; + class CheckProcHandler + { + public: + CheckProcHandler(AuraCheckProcFnType handlerScript); + bool Call(AuraScript* auraScript, ProcEventInfo& eventInfo); + private: + AuraCheckProcFnType _HandlerScript; + }; + class AuraProcHandler + { + public: + AuraProcHandler(AuraProcFnType handlerScript); + void Call(AuraScript* auraScript, ProcEventInfo& eventInfo); + private: + AuraProcFnType _HandlerScript; + }; + class EffectProcHandler : public EffectBase + { + public: + EffectProcHandler(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName); + void Call(AuraScript* auraScript, AuraEffect const* aurEff, ProcEventInfo& eventInfo); + private: + AuraEffectProcFnType _EffectHandlerScript; + }; #define AURASCRIPT_FUNCTION_CAST_DEFINES(CLASSNAME) \ class CheckAreaTargetFunction : public AuraScript::CheckAreaTargetHandler { public: CheckAreaTargetFunction(AuraCheckAreaTargetFnType _pHandlerScript) : AuraScript::CheckAreaTargetHandler((AuraScript::AuraCheckAreaTargetFnType)_pHandlerScript) {} }; \ @@ -565,6 +600,9 @@ class AuraScript : public _SpellScript class EffectAbsorbFunction : public AuraScript::EffectAbsorbHandler { public: EffectAbsorbFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectAbsorbHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) {} }; \ class EffectManaShieldFunction : public AuraScript::EffectManaShieldHandler { public: EffectManaShieldFunction(AuraEffectAbsorbFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectManaShieldHandler((AuraScript::AuraEffectAbsorbFnType)_pEffectHandlerScript, _effIndex) {} }; \ class EffectSplitFunction : public AuraScript::EffectSplitHandler { public: EffectSplitFunction(AuraEffectSplitFnType _pEffectHandlerScript, uint8 _effIndex) : AuraScript::EffectSplitHandler((AuraScript::AuraEffectSplitFnType)_pEffectHandlerScript, _effIndex) {} }; \ + class CheckProcHandlerFunction : public AuraScript::CheckProcHandler { public: CheckProcHandlerFunction(AuraCheckProcFnType handlerScript) : AuraScript::CheckProcHandler((AuraScript::AuraCheckProcFnType)handlerScript) {} }; \ + class AuraProcHandlerFunction : public AuraScript::AuraProcHandler { public: AuraProcHandlerFunction(AuraProcFnType handlerScript) : AuraScript::AuraProcHandler((AuraScript::AuraProcFnType)handlerScript) {} }; \ + class EffectProcHandlerFunction : public AuraScript::EffectProcHandler { public: EffectProcHandlerFunction(AuraEffectProcFnType effectHandlerScript, uint8 effIndex, uint16 effName) : AuraScript::EffectProcHandler((AuraScript::AuraEffectProcFnType)effectHandlerScript, effIndex, effName) {} }; \ #define PrepareAuraScript(CLASSNAME) AURASCRIPT_FUNCTION_TYPE_DEFINES(CLASSNAME) AURASCRIPT_FUNCTION_CAST_DEFINES(CLASSNAME) @@ -695,6 +733,36 @@ class AuraScript : public _SpellScript HookList<EffectSplitHandler> OnEffectSplit; #define AuraEffectSplitFn(F, I) EffectSplitFunction(&F, I) + // executed when aura checks if it can proc + // example: DoCheckProc += AuraCheckProcFn(class::function); + // where function is: bool function (ProcEventInfo& eventInfo); + HookList<CheckProcHandler> DoCheckProc; + #define AuraCheckProcFn(F) CheckProcHandlerFunction(&F) + + // executed before aura procs (possibility to prevent charge drop/cooldown) + // example: DoPrepareProc += AuraProcFn(class::function); + // where function is: void function (ProcEventInfo& eventInfo); + HookList<AuraProcHandler> DoPrepareProc; + // executed when aura procs + // example: OnProc += AuraProcFn(class::function); + // where function is: void function (ProcEventInfo& eventInfo); + HookList<AuraProcHandler> OnProc; + // executed after aura proced + // example: AfterProc += AuraProcFn(class::function); + // where function is: void function (ProcEventInfo& eventInfo); + HookList<AuraProcHandler> AfterProc; + #define AuraProcFn(F) AuraProcHandlerFunction(&F) + + // executed when aura effect procs + // example: OnEffectProc += AuraEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier); + // where function is: void function (AuraEffect const* aurEff, ProcEventInfo& procInfo); + HookList<EffectProcHandler> OnEffectProc; + // executed after aura effect proced + // example: AfterEffectProc += AuraEffectProcFn(class::function, EffectIndexSpecifier, EffectAuraNameSpecifier); + // where function is: void function (AuraEffect const* aurEff, ProcEventInfo& procInfo); + HookList<EffectProcHandler> AfterEffectProc; + #define AuraEffectProcFn(F, I, N) EffectProcHandlerFunction(&F, I, N) + // AuraScript interface - hook/effect execution manipulators // prevents default action of a hook from being executed (works only while called in a hook which default action can be prevented) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 068bca2692f..26a7986bcb8 100644 --- 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 } @@ -222,7 +225,7 @@ void World::AddSession(WorldSession* s) void World::AddSession_(WorldSession* s) { - ASSERT (s); + ASSERT(s); //NOTE - Still there is race condition in WorldSession* being used in the Sockets @@ -257,7 +260,7 @@ void World::AddSession_(WorldSession* s) uint32 Sessions = GetActiveAndQueuedSessionCount(); uint32 pLimit = GetPlayerAmountLimit(); - uint32 QueueSize = GetQueuedSessionCount(); //number of players in the queue + uint32 QueueSize = GetQueuedSessionCount(); //number of players in the queue //so we don't count the user trying to //login as a session and queue the socket that we are using @@ -266,7 +269,7 @@ void World::AddSession_(WorldSession* s) if (pLimit > 0 && Sessions >= pLimit && AccountMgr::IsPlayerAccount(s->GetSecurity()) && !HasRecentlyDisconnected(s)) { - AddQueuedPlayer (s); + AddQueuedPlayer(s); UpdateMaxSessionCounters(); sLog->outInfo(LOG_FILTER_GENERAL, "PlayerQueue: Account id %u is in Queue Position (%u).", s->GetAccountId(), ++QueueSize); return; @@ -772,7 +775,7 @@ void World::LoadConfigSettings(bool reload) if (int32(m_int_configs[CONFIG_START_PLAYER_MONEY]) < 0) { sLog->outError(LOG_FILTER_SERVER_LOADING, "StartPlayerMoney (%i) must be in range 0..%u. Set to %u.", m_int_configs[CONFIG_START_PLAYER_MONEY], MAX_MONEY_AMOUNT, 0); - m_int_configs[CONFIG_START_PLAYER_MONEY] = 0; + m_int_configs[CONFIG_START_PLAYER_MONEY] = 0; } else if (m_int_configs[CONFIG_START_PLAYER_MONEY] > MAX_MONEY_AMOUNT) { @@ -1118,6 +1121,15 @@ void World::LoadConfigSettings(bool reload) if (dataPath.at(dataPath.length()-1) != '/' && dataPath.at(dataPath.length()-1) != '\\') dataPath.push_back('/'); +#if PLATFORM == PLATFORM_UNIX || PLATFORM == PLATFORM_APPLE + if (dataPath[0] == '~') + { + const char* home = getenv("HOME"); + if (home) + dataPath.replace(0, 1, home); + } +#endif + if (reload) { if (dataPath != m_dataPath) @@ -1129,22 +1141,23 @@ 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", false); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "WORLD: MMap data directory is: %smmaps", m_dataPath.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); bool enableHeight = ConfigMgr::GetBoolDefault("vmap.enableHeight", true); - bool enablePetLOS = ConfigMgr::GetBoolDefault("vmap.petLOS", true); if (!enableHeight) sLog->outError(LOG_FILTER_SERVER_LOADING, "VMap height checking disabled! Creatures movements and other various things WILL be broken! Expect no support."); VMAP::VMapFactory::createOrGetVMapManager()->setEnableLineOfSightCalc(enableLOS); VMAP::VMapFactory::createOrGetVMapManager()->setEnableHeightCalc(enableHeight); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight: %i, getHeight: %i, indoorCheck: %i PetLOS: %i", enableLOS, enableHeight, enableIndoor, enablePetLOS); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap support included. LineOfSight: %i, getHeight: %i, indoorCheck: %i", enableLOS, enableHeight, enableIndoor); sLog->outInfo(LOG_FILTER_SERVER_LOADING, "VMap data directory is: %svmaps", m_dataPath.c_str()); m_int_configs[CONFIG_MAX_WHO] = ConfigMgr::GetIntDefault("MaxWhoListReturns", 49); - m_bool_configs[CONFIG_PET_LOS] = ConfigMgr::GetBoolDefault("vmap.petLOS", true); m_bool_configs[CONFIG_START_ALL_SPELLS] = ConfigMgr::GetBoolDefault("PlayerStart.AllSpells", false); if (m_bool_configs[CONFIG_START_ALL_SPELLS]) sLog->outWarn(LOG_FILTER_SERVER_LOADING, "PlayerStart.AllSpells enabled - may not function as intended!"); @@ -1236,6 +1249,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(); @@ -1276,12 +1292,7 @@ void World::SetInitialWorldSettings() //No SQL injection as values are treated as integers // not send custom type REALM_FFA_PVP to realm list - uint32 server_type; - if (IsFFAPvPRealm()) - server_type = REALM_TYPE_PVP; - else - server_type = getIntConfig(CONFIG_GAME_TYPE); - + uint32 server_type = IsFFAPvPRealm() ? uint32(REALM_TYPE_PVP) : getIntConfig(CONFIG_GAME_TYPE); uint32 realm_zone = getIntConfig(CONFIG_REALM_ZONE); LoginDatabase.PExecute("UPDATE realmlist SET icon = %u, timezone = %u WHERE id = '%d'", server_type, realm_zone, realmID); // One-time query @@ -1382,8 +1393,8 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Item Random Enchantments Table..."); LoadRandomEnchantmentsTable(); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Disables"); - DisableMgr::LoadDisables(); // must be before loading quests and items + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Disables"); // must be before loading quests and items + DisableMgr::LoadDisables(); sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Items..."); // must be after LoadRandomEnchantmentsTable and LoadPageTexts sObjectMgr->LoadItemTemplates(); @@ -1563,9 +1574,11 @@ void World::SetInitialWorldSettings() ///- Load dynamic data tables from the database sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Item Auctions..."); sAuctionMgr->LoadAuctionItems(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Auctions..."); sAuctionMgr->LoadAuctions(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Guilds..."); sGuildMgr->LoadGuilds(); sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading ArenaTeams..."); @@ -1781,7 +1794,9 @@ void World::SetInitialWorldSettings() uint32 startupDuration = GetMSTimeDiffToNow(startupBegin); sLog->outInfo(LOG_FILTER_WORLDSERVER, "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000)); - sLog->EnableDBAppenders(); + + if (uint32 realmId = ConfigMgr::GetIntDefault("RealmID", 0)) // 0 reserved for auth + sLog->SetRealmId(realmId); } void World::DetectDBCLang() @@ -1825,7 +1840,6 @@ void World::DetectDBCLang() m_defaultDbcLocale = LocaleConstant(default_locale); sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Using %s DBC Locale as default. All available DBC locales: %s", localeNames[m_defaultDbcLocale], availableLocalsStr.empty() ? "<none>" : availableLocalsStr.c_str()); - } void World::RecordTimeDiff(const char *text, ...) @@ -1865,7 +1879,6 @@ void World::LoadAutobroadcasts() if (!result) { sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty!"); - return; } @@ -1873,7 +1886,6 @@ void World::LoadAutobroadcasts() do { - Field* fields = result->Fetch(); std::string message = fields[0].GetString(); @@ -1882,8 +1894,7 @@ void World::LoadAutobroadcasts() ++count; } while (result->NextRow()); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u autobroadcasts definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); - + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u autobroadcast definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } /// Update the World ! @@ -2933,8 +2944,8 @@ void World::LoadDBVersion() if (result) { Field* fields = result->Fetch(); - m_DBVersion = fields[0].GetString(); + m_DBVersion = fields[0].GetString(); // will be overwrite by config values if different and non-0 m_int_configs[CONFIG_CLIENTCACHE_VERSION] = fields[1].GetUInt32(); } diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 768f1d327b8..4a7629cb3af 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -138,7 +138,6 @@ enum WorldBoolConfigs CONFIG_ARENA_LOG_EXTENDED_INFO, CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, CONFIG_VMAP_INDOOR_CHECK, - CONFIG_PET_LOS, CONFIG_START_ALL_SPELLS, CONFIG_START_ALL_EXPLORED, CONFIG_START_ALL_REP, @@ -164,6 +163,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 9726cc1a937..cf4618022b3 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -45,6 +45,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/Commands/CMakeLists.txt b/src/server/scripts/Commands/CMakeLists.txt index 97e9b35e6cd..7b9e2444952 100644 --- a/src/server/scripts/Commands/CMakeLists.txt +++ b/src/server/scripts/Commands/CMakeLists.txt @@ -42,6 +42,7 @@ set(scripts_STAT_SRCS Commands/cs_server.cpp Commands/cs_titles.cpp Commands/cs_wp.cpp + Commands/cs_mmaps.cpp # Commands/cs_pdump.cpp # Commands/cs_channel.cpp # Commands/cs_pet.cpp diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp index 37e282cac8e..34738777c85 100644 --- a/src/server/scripts/Commands/cs_disable.cpp +++ b/src/server/scripts/Commands/cs_disable.cpp @@ -48,6 +48,7 @@ public: { "achievement_criteria", SEC_ADMINISTRATOR, true, &HandleRemoveDisableAchievementCriteriaCommand, "", NULL }, { "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleRemoveDisableOutdoorPvPCommand, "", NULL }, { "vmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableVmapCommand, "", NULL }, + { "mmap", SEC_ADMINISTRATOR, true, &HandleRemoveDisableMMapCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; static ChatCommand addDisableCommandTable[] = @@ -59,6 +60,7 @@ public: { "achievement_criteria", SEC_ADMINISTRATOR, true, &HandleAddDisableAchievementCriteriaCommand, "", NULL }, { "outdoorpvp", SEC_ADMINISTRATOR, true, &HandleAddDisableOutdoorPvPCommand, "", NULL }, { "vmap", SEC_ADMINISTRATOR, true, &HandleAddDisableVmapCommand, "", NULL }, + { "mmap", SEC_ADMINISTRATOR, true, &HandleAddDisableMMapCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; static ChatCommand disableCommandTable[] = @@ -172,6 +174,17 @@ public: disableTypeStr = "vmap"; break; } + case DISABLE_TYPE_MMAP: + { + if (!sMapStore.LookupEntry(entry)) + { + handler->PSendSysMessage(LANG_COMMAND_NOMAPFOUND); + handler->SetSentErrorMessage(true); + return false; + } + disableTypeStr = "mmap"; + break; + } default: break; } @@ -256,6 +269,14 @@ public: return HandleAddDisables(handler, args, DISABLE_TYPE_VMAP); } + static bool HandleAddDisableMMapCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + return HandleAddDisables(handler, args, DISABLE_TYPE_MMAP); + } + static bool HandleRemoveDisables(ChatHandler* handler, char const* args, uint8 disableType) { char* entryStr = strtok((char*)args, " "); @@ -289,6 +310,9 @@ public: case DISABLE_TYPE_VMAP: disableTypeStr = "vmap"; break; + case DISABLE_TYPE_MMAP: + disableTypeStr = "mmap"; + break; } PreparedStatement* stmt = NULL; @@ -367,6 +391,14 @@ public: return HandleRemoveDisables(handler, args, DISABLE_TYPE_VMAP); } + + static bool HandleRemoveDisableMMapCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + return HandleRemoveDisables(handler, args, DISABLE_TYPE_MMAP); + } }; void AddSC_disable_commandscript() diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 54fe41a8a4d..e2b5ac9487d 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -46,6 +46,7 @@ public: { "leader", SEC_ADMINISTRATOR, false, &HandleGroupLeaderCommand, "", NULL }, { "disband", SEC_ADMINISTRATOR, false, &HandleGroupDisbandCommand, "", NULL }, { "remove", SEC_ADMINISTRATOR, false, &HandleGroupRemoveCommand, "", NULL }, + { "join", SEC_ADMINISTRATOR, false, &HandleGroupJoinCommand, "", NULL }, { NULL, 0, false, NULL, "", NULL } }; static ChatCommand petCommandTable[] = @@ -2725,6 +2726,61 @@ public: return true; } + static bool HandleGroupJoinCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + Player* playerSource = NULL; + Player* playerTarget = NULL; + Group* groupSource = NULL; + Group* groupTarget = NULL; + uint64 guidSource = 0; + uint64 guidTarget = 0; + char* nameplgrStr = strtok((char*)args, " "); + char* nameplStr = strtok(NULL, " "); + + if (handler->GetPlayerGroupAndGUIDByName(nameplgrStr, playerSource, groupSource, guidSource, true)) + { + if (groupSource) + { + if (handler->GetPlayerGroupAndGUIDByName(nameplStr, playerTarget, groupTarget, guidTarget, true)) + { + if (!groupTarget && playerTarget->GetGroup() != groupSource) + { + if (!groupSource->IsFull()) + { + groupSource->AddMember(playerTarget); + groupSource->BroadcastGroupUpdate(); + handler->PSendSysMessage(LANG_GROUP_PLAYER_JOINED, playerTarget->GetName().c_str(), playerSource->GetName().c_str()); + return true; + } + else + { + // group is full + handler->PSendSysMessage(LANG_GROUP_FULL); + return true; + } + } + else + { + // group is full or target player already in a group + handler->PSendSysMessage(LANG_GROUP_ALREADY_IN_GROUP, playerTarget->GetName().c_str()); + return true; + } + } + } + else + { + // specified source player is not in a group + handler->PSendSysMessage(LANG_GROUP_NOT_IN_GROUP, playerSource->GetName().c_str()); + return true; + } + } + + return true; + } + static bool HandlePlayAllCommand(ChatHandler* handler, char const* args) { if (!*args) diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp new file mode 100644 index 00000000000..97861133983 --- /dev/null +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** +* @file cs_mmaps.cpp +* @brief .mmap related commands +* +* This file contains the CommandScripts for all +* mmap sub-commands +*/ + +#include "ScriptMgr.h" +#include "Chat.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "PointMovementGenerator.h" +#include "PathGenerator.h" +#include "MMapFactory.h" +#include "Map.h" +#include "TargetedMovementGenerator.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" + +class mmaps_commandscript : public CommandScript +{ +public: + mmaps_commandscript() : CommandScript("mmaps_commandscript") { } + + ChatCommand* GetCommands() const + { + static ChatCommand mmapCommandTable[] = + { + { "path", SEC_ADMINISTRATOR, false, &HandleMmapPathCommand, "", NULL }, + { "loc", SEC_ADMINISTRATOR, false, &HandleMmapLocCommand, "", NULL }, + { "loadedtiles", SEC_ADMINISTRATOR, false, &HandleMmapLoadedTilesCommand, "", NULL }, + { "stats", SEC_ADMINISTRATOR, false, &HandleMmapStatsCommand, "", NULL }, + { "testarea", SEC_ADMINISTRATOR, false, &HandleMmapTestArea, "", NULL }, + { NULL, 0, false, NULL, "", NULL } + }; + + static ChatCommand commandTable[] = + { + { "mmap", SEC_ADMINISTRATOR, true, NULL, "", mmapCommandTable }, + { NULL, 0, false, NULL, "", NULL } + }; + return commandTable; + } + + static bool HandleMmapPathCommand(ChatHandler* handler, char const* args) + { + if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + handler->PSendSysMessage("mmap path:"); + + // units + Player* player = handler->GetSession()->GetPlayer(); + Unit* target = handler->getSelectedUnit(); + if (!player || !target) + { + handler->PSendSysMessage("Invalid target/source selection."); + return true; + } + + char* para = strtok((char*)args, " "); + + bool useStraightPath = false; + if (para && strcmp(para, "true") == 0) + useStraightPath = true; + + // unit locations + float x, y, z; + player->GetPosition(x, y, z); + + // path + PathGenerator path(target); + path.SetUseStraightPath(useStraightPath); + bool result = path.CalculatePath(x, y, z); + + PointsArray pointPath = path.GetPath(); + handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str()); + handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : "SmoothPath"); + handler->PSendSysMessage("Result: %s - Length: "SIZEFMTD" - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType()); + + Vector3 start = path.GetStartPosition(); + Vector3 end = path.GetEndPosition(); + Vector3 actualEnd = path.GetActualEndPosition(); + + handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z); + handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z); + handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z); + + if (!player->isGameMaster()) + handler->PSendSysMessage("Enable GM mode to see the path points."); + + for (uint32 i = 0; i < pointPath.size(); ++i) + player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000); + + return true; + } + + static bool HandleMmapLocCommand(ChatHandler* handler, char const* /*args*/) + { + handler->PSendSysMessage("mmap tileloc:"); + + // grid tile location + Player* player = handler->GetSession()->GetPlayer(); + + int32 gx = 32 - player->GetPositionX() / SIZE_OF_GRIDS; + int32 gy = 32 - player->GetPositionY() / SIZE_OF_GRIDS; + + handler->PSendSysMessage("%03u%02i%02i.mmtile", player->GetMapId(), gy, gx); + handler->PSendSysMessage("gridloc [%i,%i]", gx, gy); + + // calculate navmesh tile location + dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); + dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId()); + if (!navmesh || !navmeshquery) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + float const* min = navmesh->getParams()->orig; + float x, y, z; + player->GetPosition(x, y, z); + float location[VERTEX_SIZE] = {y, z, x}; + float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; + + int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS); + int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS); + + handler->PSendSysMessage("Calc [%02i,%02i]", tilex, tiley); + + // navmesh poly -> navmesh tile location + dtQueryFilter filter = dtQueryFilter(); + dtPolyRef polyRef = INVALID_POLYREF; + navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, NULL); + + if (polyRef == INVALID_POLYREF) + handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)"); + else + { + dtMeshTile const* tile; + dtPoly const* poly; + navmesh->getTileAndPolyByRef(polyRef, &tile, &poly); + if (tile) + handler->PSendSysMessage("Dt [%02i,%02i]", tile->header->x, tile->header->y); + else + handler->PSendSysMessage("Dt [??,??] (no tile loaded)"); + } + + return true; + } + + static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/) + { + uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId(); + dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid); + dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId()); + if (!navmesh || !navmeshquery) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + handler->PSendSysMessage("mmap loadedtiles:"); + + for (int32 i = 0; i < navmesh->getMaxTiles(); ++i) + { + dtMeshTile const* tile = navmesh->getTile(i); + if (!tile || !tile->header) + continue; + + handler->PSendSysMessage("[%02i,%02i]", tile->header->x, tile->header->y); + } + + return true; + } + + static bool HandleMmapStatsCommand(ChatHandler* handler, char const* /*args*/) + { + uint32 mapId = handler->GetSession()->GetPlayer()->GetMapId(); + handler->PSendSysMessage("mmap stats:"); + handler->PSendSysMessage(" global mmap pathfinding is %sabled", MMAP::MMapFactory::IsPathfindingEnabled(mapId) ? "en" : "dis"); + + MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager(); + handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount()); + + dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); + if (!navmesh) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + uint32 tileCount = 0; + uint32 nodeCount = 0; + uint32 polyCount = 0; + uint32 vertCount = 0; + uint32 triCount = 0; + uint32 triVertCount = 0; + uint32 dataSize = 0; + for (int32 i = 0; i < navmesh->getMaxTiles(); ++i) + { + dtMeshTile const* tile = navmesh->getTile(i); + if (!tile || !tile->header) + continue; + + tileCount++; + nodeCount += tile->header->bvNodeCount; + polyCount += tile->header->polyCount; + vertCount += tile->header->vertCount; + triCount += tile->header->detailTriCount; + triVertCount += tile->header->detailVertCount; + dataSize += tile->dataSize; + } + + handler->PSendSysMessage("Navmesh stats:"); + handler->PSendSysMessage(" %u tiles loaded", tileCount); + handler->PSendSysMessage(" %u BVTree nodes", nodeCount); + handler->PSendSysMessage(" %u polygons (%u vertices)", polyCount, vertCount); + handler->PSendSysMessage(" %u triangles (%u vertices)", triCount, triVertCount); + handler->PSendSysMessage(" %.2f MB of data (not including pointers)", ((float)dataSize / sizeof(unsigned char)) / 1048576); + + return true; + } + + static bool HandleMmapTestArea(ChatHandler* handler, char const* /*args*/) + { + float radius = 40.0f; + WorldObject* object = handler->GetSession()->GetPlayer(); + + CellCoord pair(Trinity::ComputeCellCoord(object->GetPositionX(), object->GetPositionY())); + Cell cell(pair); + cell.SetNoCreate(); + + std::list<Creature*> creatureList; + + Trinity::AnyUnitInObjectRangeCheck go_check(object, radius); + Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck> go_search(object, creatureList, go_check); + TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyUnitInObjectRangeCheck>, GridTypeMapContainer> go_visit(go_search); + + // Get Creatures + cell.Visit(pair, go_visit, *(object->GetMap()), *object, radius); + + if (!creatureList.empty()) + { + handler->PSendSysMessage("Found "SIZEFMTD" Creatures.", creatureList.size()); + + uint32 paths = 0; + uint32 uStartTime = getMSTime(); + + float gx, gy, gz; + object->GetPosition(gx, gy, gz); + for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + { + PathGenerator path(*itr); + path.CalculatePath(gx, gy, gz); + ++paths; + } + + uint32 uPathLoadTime = getMSTimeDiff(uStartTime, getMSTime()); + handler->PSendSysMessage("Generated %i paths in %i ms", paths, uPathLoadTime); + } + else + handler->PSendSysMessage("No creatures in %f yard range.", radius); + + return true; + } +}; + +void AddSC_mmaps_commandscript() +{ + new mmaps_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 8679e288282..7ccd5ed1177 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -33,6 +33,42 @@ EndScriptData */ #include "Player.h" #include "Pet.h" +struct NpcFlagText +{ + uint32 flag; + int32 text; +}; + +#define NPCFLAG_COUNT 24 + +const NpcFlagText npcFlagTexts[NPCFLAG_COUNT] = +{ + { UNIT_NPC_FLAG_AUCTIONEER, LANG_NPCINFO_AUCTIONEER }, + { UNIT_NPC_FLAG_BANKER, LANG_NPCINFO_BANKER }, + { UNIT_NPC_FLAG_BATTLEMASTER, LANG_NPCINFO_BATTLEMASTER }, + { UNIT_NPC_FLAG_FLIGHTMASTER, LANG_NPCINFO_FLIGHTMASTER }, + { UNIT_NPC_FLAG_GOSSIP, LANG_NPCINFO_GOSSIP }, + { UNIT_NPC_FLAG_GUILD_BANKER, LANG_NPCINFO_GUILD_BANKER }, + { UNIT_NPC_FLAG_INNKEEPER, LANG_NPCINFO_INNKEEPER }, + { UNIT_NPC_FLAG_PETITIONER, LANG_NPCINFO_PETITIONER }, + { UNIT_NPC_FLAG_PLAYER_VEHICLE, LANG_NPCINFO_PLAYER_VEHICLE }, + { UNIT_NPC_FLAG_QUESTGIVER, LANG_NPCINFO_QUESTGIVER }, + { UNIT_NPC_FLAG_REPAIR, LANG_NPCINFO_REPAIR }, + { UNIT_NPC_FLAG_SPELLCLICK, LANG_NPCINFO_SPELLCLICK }, + { UNIT_NPC_FLAG_SPIRITGUIDE, LANG_NPCINFO_SPIRITGUIDE }, + { UNIT_NPC_FLAG_SPIRITHEALER, LANG_NPCINFO_SPIRITHEALER }, + { UNIT_NPC_FLAG_STABLEMASTER, LANG_NPCINFO_STABLEMASTER }, + { UNIT_NPC_FLAG_TABARDDESIGNER, LANG_NPCINFO_TABARDDESIGNER }, + { UNIT_NPC_FLAG_TRAINER, LANG_NPCINFO_TRAINER }, + { UNIT_NPC_FLAG_TRAINER_CLASS, LANG_NPCINFO_TRAINER_CLASS }, + { UNIT_NPC_FLAG_TRAINER_PROFESSION, LANG_NPCINFO_TRAINER_PROFESSION }, + { UNIT_NPC_FLAG_VENDOR, LANG_NPCINFO_VENDOR }, + { UNIT_NPC_FLAG_VENDOR_AMMO, LANG_NPCINFO_VENDOR_AMMO }, + { UNIT_NPC_FLAG_VENDOR_FOOD, LANG_NPCINFO_VENDOR_FOOD }, + { UNIT_NPC_FLAG_VENDOR_POISON, LANG_NPCINFO_VENDOR_POISON }, + { UNIT_NPC_FLAG_VENDOR_REAGENT, LANG_NPCINFO_VENDOR_REAGENT } +}; + class npc_commandscript : public CommandScript { public: @@ -619,11 +655,9 @@ public: handler->PSendSysMessage(LANG_NPCINFO_POSITION, float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ())); handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str()); - if (npcflags & UNIT_NPC_FLAG_VENDOR) - handler->SendSysMessage(LANG_NPCINFO_VENDOR); - - if (npcflags & UNIT_NPC_FLAG_TRAINER) - handler->SendSysMessage(LANG_NPCINFO_TRAINER); + for (uint8 i = 0; i < NPCFLAG_COUNT; i++) + if (npcflags & npcFlagTexts[i].flag) + handler->PSendSysMessage(npcFlagTexts[i].text, npcFlagTexts[i].flag); return true; } diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp index b0a2d48d053..19660cec4af 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_kirtonos_the_herald.cpp @@ -64,6 +64,12 @@ enum eMisc KIRTONOS_PATH = 105061 }; +Position const PosMove[2] = +{ + { 299.4884f, 92.76137f, 105.6335f, 0.0f }, + { 314.8673f, 90.30210f, 101.6459f, 0.0f } +}; + class boss_kirtonos_the_herald : public CreatureScript { public: boss_kirtonos_the_herald() : CreatureScript("boss_kirtonos_the_herald") { } @@ -74,15 +80,11 @@ class boss_kirtonos_the_herald : public CreatureScript void Reset() { - _introEvent = 0; - _introTimer = 0; _Reset(); } void EnterCombat(Unit* /*who*/) { - _introTimer = 0; - _introEvent = 0; events.ScheduleEvent(EVENT_SWOOP, urand(8000, 8000)); events.ScheduleEvent(EVENT_WING_FLAP, urand(15000, 15000)); events.ScheduleEvent(EVENT_PIERCE_ARMOR, urand(18000, 18000)); @@ -120,12 +122,10 @@ class boss_kirtonos_the_herald : public CreatureScript void IsSummonedBy(Unit* /*summoner*/) { + events.ScheduleEvent(INTRO_1, 500); me->SetDisableGravity(true); me->SetReactState(REACT_PASSIVE); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - _introEvent = INTRO_1; - _introTimer = 1; - _currentPoint = 0; Talk(EMOTE_SUMMONED); } @@ -138,68 +138,58 @@ class boss_kirtonos_the_herald : public CreatureScript { if (type == WAYPOINT_MOTION_TYPE && id == POINT_KIRTONOS_LAND) { - _introTimer = 1500; - _introEvent = INTRO_2; + events.ScheduleEvent(INTRO_2, 1500); } } void UpdateAI(uint32 const diff) { - if (_introEvent) + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent() && !UpdateVictim()) { - if (_introTimer <= diff) + switch (eventId) { - switch (_introEvent) - { - case INTRO_1: - me->GetMotionMaster()->MovePath(KIRTONOS_PATH, false); - _introEvent = 0; - break; - case INTRO_2: - me->GetMotionMaster()->MovePoint(0, 299.4884f, 92.76137f, 105.6335f); - _introTimer = 1000; - _introEvent = INTRO_3; - break; - case INTRO_3: - if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_GATE_KIRTONOS))) - gate->SetGoState(GO_STATE_READY); - me->SetFacingTo(0.01745329f); - _introTimer = 3000; - _introEvent = INTRO_4; - break; - case INTRO_4: - if (GameObject* brazier = me->GetMap()->GetGameObject(instance->GetData64(GO_BRAZIER_OF_THE_HERALD))) - brazier->SetGoState(GO_STATE_READY); - me->SetWalk(true); - me->SetDisableGravity(false); - DoCast(me, SPELL_KIRTONOS_TRANSFORM); - me->SetCanFly(false); - _introTimer = 1000; - _introEvent = INTRO_5; - break; - case INTRO_5: - me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); - me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_KIRTONOS_STAFF)); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); - me->SetReactState(REACT_AGGRESSIVE); - _introTimer = 5000; - _introEvent = INTRO_6; - case INTRO_6: - me->GetMotionMaster()->MovePoint(0, 314.8673f, 90.3021f, 101.6459f); - _introTimer = 0; - _introEvent = 0; + case INTRO_1: + me->GetMotionMaster()->MovePath(KIRTONOS_PATH, false); + break; + case INTRO_2: + me->GetMotionMaster()->MovePoint(0, PosMove[0]); + events.ScheduleEvent(INTRO_3, 1000); + break; + case INTRO_3: + if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_GATE_KIRTONOS))) + gate->SetGoState(GO_STATE_READY); + me->SetFacingTo(0.01745329f); + events.ScheduleEvent(INTRO_4, 3000); + break; + case INTRO_4: + if (GameObject* brazier = me->GetMap()->GetGameObject(instance->GetData64(GO_BRAZIER_OF_THE_HERALD))) + brazier->SetGoState(GO_STATE_READY); + me->SetWalk(true); + me->SetDisableGravity(false); + DoCast(me, SPELL_KIRTONOS_TRANSFORM); + me->SetCanFly(false); + events.ScheduleEvent(INTRO_5, 1000); + break; + case INTRO_5: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_KIRTONOS_STAFF)); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + events.ScheduleEvent(INTRO_6, 5000); + break; + case INTRO_6: + me->GetMotionMaster()->MovePoint(0, PosMove[1]); + break; + default: break; - } } - else - _introTimer -= diff; } if (!UpdateVictim()) return; - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) return; @@ -257,11 +247,6 @@ class boss_kirtonos_the_herald : public CreatureScript DoMeleeAttackIfReady(); } - - private: - uint8 _introEvent; - uint32 _introTimer; - uint32 _currentPoint; }; CreatureAI* GetAI(Creature* creature) const @@ -280,6 +265,11 @@ enum Brazier_Of_The_Herald SOUND_SCREECH = 557 }; +Position const PosSummon[1] = +{ + { 315.028f, 70.53845f, 102.1496f, 0.3859715f } +}; + class go_brazier_of_the_herald : public GameObjectScript { public: @@ -289,7 +279,7 @@ class go_brazier_of_the_herald : public GameObjectScript { go->UseDoorOrButton(); go->PlayDirectSound(SOUND_SCREECH, 0); - player->SummonCreature(NPC_KIRTONOS, 315.028f, 70.53845f, 102.1496f, 0.3859715f, TEMPSUMMON_DEAD_DESPAWN, 900000); + player->SummonCreature(NPC_KIRTONOS, PosSummon[0], TEMPSUMMON_DEAD_DESPAWN, 900000); return true; } }; diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp index 1334d587464..c2806c395f4 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_arlokk.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> * * 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 @@ -17,60 +16,74 @@ */ /* ScriptData -SDName: Boss_Arlokk -SD%Complete: 95 -SDComment: Wrong cleave and red aura is missing. -SDCategory: Zul'Gurub +TCName: Boss_Arlokk +TC%Complete: 95 +TCComment: Wrong cleave and red aura is missing not yet added. +TCComment: Prowlers moving through wall hopefully mmaps will fix. +TCComment: Can't test LOS until mmaps. +TCCategory: Zul'Gurub EndScriptData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellInfo.h" #include "zulgurub.h" enum Says { SAY_AGGRO = 0, - SAY_FEAST_PANTHER = 1, + SAY_FEAST_PROWLER = 1, SAY_DEATH = 2 }; enum Spells { - SPELL_SHADOW_WORD_PAIN = 23952, - SPELL_GOUGE = 24698, - SPELL_MARK = 24210, - SPELL_CLEAVE = 26350, // Perhaps not right. Not a red aura... - SPELL_PANTHER_TRANSFORM = 24190 + SPELL_SHADOW_WORD_PAIN = 24212, // Corrected + SPELL_GOUGE = 12540, // Corrected + SPELL_MARK_OF_ARLOKK = 24210, // triggered spell 24211 Added to spell_dbc + SPELL_RAVAGE = 24213, // Corrected + SPELL_CLEAVE = 25174, // Searching for right spell + SPELL_PANTHER_TRANSFORM = 24190, // Transform to panther now used + SPELL_SUMMON_PROWLER = 24246, // Added to Spell_dbc + SPELL_VANISH_VISUAL = 24222, // Added + SPELL_VANISH = 24223, // Added + SPELL_SUPER_INVIS = 24235 // Added to Spell_dbc }; enum Events { - EVENT_SHADOW_WORD_PAIN = 0, - EVENT_GOUGE = 1, - EVENT_MARK = 2, - EVENT_CLEAVE = 3, - EVENT_VANISH = 4, - EVENT_VISIBLE = 5, - EVENT_SUMMON = 6 + EVENT_SHADOW_WORD_PAIN = 1, + EVENT_GOUGE = 2, + EVENT_MARK_OF_ARLOKK = 3, + EVENT_RAVAGE = 4, + EVENT_TRANSFORM = 5, + EVENT_VANISH = 6, + EVENT_VANISH_2 = 7, + EVENT_TRANSFORM_BACK = 8, + EVENT_VISIBLE = 9, + EVENT_SUMMON_PROWLERS = 10 }; enum Phases { - PHASE_ONE = 1, - PHASE_TWO = 2 + PHASE_ALL = 0, + PHASE_ONE = 1, + PHASE_TWO = 2 }; -enum ModelIds +enum Weapon { - MODEL_ID_NORMAL = 15218, - MODEL_ID_PANTHER = 15215, - MODEL_ID_BLANK = 11686 + WEAPON_DAGGER = 10616 }; -Position const PosSummonProwlers[2] = +enum Misc { - { -11532.7998f, -1649.6734f, 41.4800f, 0.0f }, - { -11532.9970f, -1606.4840f, 41.2979f, 0.0f } + MAX_PROWLERS_PER_SIDE = 15 +}; + +Position const PosMoveOnSpawn[1] = +{ + { -11561.9f, -1627.868f, 41.29941f, 0.0f } }; class boss_arlokk : public CreatureScript @@ -79,58 +92,92 @@ class boss_arlokk : public CreatureScript struct boss_arlokkAI : public BossAI { - boss_arlokkAI(Creature* creature) : BossAI(creature, DATA_ARLOKK) {} - - uint32 summonCount; - // Unit* markedTarget; - uint64 markedTargetGUID; + boss_arlokkAI(Creature* creature) : BossAI(creature, DATA_ARLOKK) { } void Reset() { - summonCount = 0; - markedTargetGUID = 0; - me->SetDisplayId(MODEL_ID_NORMAL); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + _summonCountA = 0; + _summonCountB = 0; + me->RemoveAllAuras(); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER)); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER)); + if (instance) + { + if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_FORCEFIELD))) + gate->SetGoState(GO_STATE_READY); + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(0, PosMoveOnSpawn[0]); + } } void JustDied(Unit* /*killer*/) { - _JustDied(); Talk(SAY_DEATH); - me->SetDisplayId(MODEL_ID_NORMAL); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveAllAuras(); + if (instance) + { + if (GameObject* gate = me->GetMap()->GetGameObject(instance->GetData64(GO_FORCEFIELD))) + gate->SetGoState(GO_STATE_ACTIVE); + instance->SetBossState(DATA_ARLOKK, DONE); + } } void EnterCombat(Unit* /*who*/) { _EnterCombat(); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 8000, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_MARK, 35000, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_SUMMON, 5000); - events.ScheduleEvent(EVENT_VANISH, 60000); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(7000, 9000), 0, PHASE_ONE); + events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE); + if (instance) + events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL); + events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(9000, 11000), 0, PHASE_ALL); + events.ScheduleEvent(EVENT_TRANSFORM, urand(15000, 20000), 0, PHASE_ONE); Talk(SAY_AGGRO); - } - void JustReachedHome() - { - if (instance) - instance->SetData(DATA_ARLOKK, NOT_STARTED); - me->DespawnOrUnsummon(); + // Sets up list of Panther spawners to cast on + std::list<Creature*> triggerList; + GetCreatureListWithEntryInGrid(triggerList, me, NPC_PANTHER_TRIGGER, 100.0f); + if (!triggerList.empty()) + { + uint8 sideA = 0; + uint8 sideB = 0; + for (std::list<Creature*>::const_iterator itr = triggerList.begin(); itr != triggerList.end(); ++itr) + { + if (Creature* trigger = *itr) + { + if (trigger->GetPositionY() < -1625.0f) + { + _triggersSideAGUID[sideA] = trigger->GetGUID(); + ++sideA; + } + else + { + _triggersSideBGUID[sideB] = trigger->GetGUID(); + ++sideB; + } + } + } + } } - void DoSummonPhanters() + void EnterEvadeMode() { - if (markedTargetGUID) - Talk(SAY_FEAST_PANTHER, markedTargetGUID); - me->SummonCreature(NPC_ZULIAN_PROWLER, PosSummonProwlers[0], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); - me->SummonCreature(NPC_ZULIAN_PROWLER, PosSummonProwlers[1], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if (instance) + { + if (GameObject* object = me->GetMap()->GetGameObject(instance->GetData64(GO_FORCEFIELD))) + object->SetGoState(GO_STATE_ACTIVE); + if (GameObject* object = me->GetMap()->GetGameObject(instance->GetData64(GO_GONG_OF_BETHEKK))) + object->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + instance->SetBossState(DATA_ARLOKK, NOT_STARTED); + } + me->DespawnOrUnsummon(4000); } - void JustSummoned(Creature* summoned) + void SetData(uint32 id, uint32 /*value*/) { - if (Unit* markedTarget = Unit::GetUnit(*me, markedTargetGUID)) - summoned->AI()->AttackStart(markedTarget); - ++summonCount; + if (id == 1) + --_summonCountA; + else if (id == 2) + --_summonCountB; } void UpdateAI(uint32 const diff) @@ -149,48 +196,104 @@ class boss_arlokk : public CreatureScript { case EVENT_SHADOW_WORD_PAIN: DoCastVictim(SPELL_SHADOW_WORD_PAIN, true); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 15000, 0, PHASE_ONE); - break; - case EVENT_MARK: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true), SPELL_MARK); - events.ScheduleEvent(EVENT_MARK, 15000, 0, PHASE_ONE); - break; - case EVENT_CLEAVE: - DoCastVictim(SPELL_SHADOW_WORD_PAIN, true); - events.ScheduleEvent(EVENT_CLEAVE, 16000, 0, PHASE_TWO); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(5000, 7000), 0, PHASE_ONE); break; case EVENT_GOUGE: - DoCastVictim(SPELL_SHADOW_WORD_PAIN, true); - events.ScheduleEvent(EVENT_GOUGE, urand(17000, 27000), 0, PHASE_TWO); + DoCastVictim(SPELL_GOUGE, true); break; - case EVENT_SUMMON: - if (summonCount <= 30) - DoSummonPhanters(); - events.ScheduleEvent(EVENT_SUMMON, 5000); + case EVENT_SUMMON_PROWLERS: + if (_summonCountA < MAX_PROWLERS_PER_SIDE) + { + if (Unit* trigger = me->GetUnit(*me, _triggersSideAGUID[urand(0, 4)])) + { + trigger->CastSpell(trigger, SPELL_SUMMON_PROWLER); + ++_summonCountA; + } + } + if (_summonCountB < MAX_PROWLERS_PER_SIDE) + { + if (Unit* trigger = me->GetUnit(*me, _triggersSideBGUID[urand(0, 4)])) + { + trigger->CastSpell(trigger, SPELL_SUMMON_PROWLER); + ++_summonCountB; + } + } + events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL); break; - case EVENT_VANISH: - me->SetDisplayId(MODEL_ID_BLANK); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + case EVENT_MARK_OF_ARLOKK: + { + Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, urand(1,3), 0.0f, false, -SPELL_MARK_OF_ARLOKK); + if (!target) + target = me->getVictim(); + if (target) + { + DoCast(target, SPELL_MARK_OF_ARLOKK, true); + Talk(SAY_FEAST_PROWLER, target->GetGUID()); + } + events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(120000, 130000)); + break; + } + case EVENT_TRANSFORM: + { + DoCast(me, SPELL_PANTHER_TRANSFORM); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(EQUIP_UNEQUIP)); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(EQUIP_UNEQUIP)); + const CreatureTemplate* cinfo = me->GetCreatureTemplate(); + me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35))); + me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35))); + me->UpdateDamagePhysical(BASE_ATTACK); me->AttackStop(); DoResetThreat(); - events.ScheduleEvent(EVENT_VISIBLE, 6000); + me->SetReactState(REACT_PASSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + DoCast(me, SPELL_VANISH_VISUAL); + DoCast(me, SPELL_VANISH); + events.ScheduleEvent(EVENT_VANISH, 1000, 0, PHASE_ONE); + break; + } + case EVENT_VANISH: + DoCast(me, SPELL_SUPER_INVIS); + me->SetWalk(false); + if (instance) + me->GetMotionMaster()->MovePoint(0, frand(-11551.0f, -11508.0f), frand(-1638.0f, -1617.0f), me->GetPositionZ()); + events.ScheduleEvent(EVENT_VANISH_2, 9000, 0, PHASE_ONE); + break; + case EVENT_VANISH_2: + DoCast(me, SPELL_VANISH); + DoCast(me, SPELL_SUPER_INVIS); + events.ScheduleEvent(EVENT_VISIBLE, urand(7000, 10000), 0, PHASE_ONE); break; case EVENT_VISIBLE: - { - me->SetDisplayId(MODEL_ID_PANTHER); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - const CreatureTemplate* cinfo = me->GetCreatureTemplate(); - me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35))); - me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35))); - me->UpdateDamagePhysical(BASE_ATTACK); - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - AttackStart(target); - events.ScheduleEvent(EVENT_VANISH, 39000); - events.ScheduleEvent(EVENT_CLEAVE, 0, PHASE_TWO); - events.ScheduleEvent(EVENT_GOUGE, 14000, 0, PHASE_TWO); - events.SetPhase(PHASE_TWO); - break; - } + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + AttackStart(target); + me->RemoveAura(SPELL_SUPER_INVIS); + me->RemoveAura(SPELL_VANISH); + events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO); + events.ScheduleEvent(EVENT_TRANSFORM_BACK, urand(15000, 18000), 0, PHASE_TWO); + events.SetPhase(PHASE_TWO); + break; + case EVENT_RAVAGE: + DoCastVictim(SPELL_RAVAGE, true); + events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO); + break; + case EVENT_TRANSFORM_BACK: + { + me->RemoveAura(SPELL_PANTHER_TRANSFORM); + DoCast(me, SPELL_VANISH_VISUAL); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(WEAPON_DAGGER)); + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(WEAPON_DAGGER)); + const CreatureTemplate* cinfo = me->GetCreatureTemplate(); + me->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg)); + me->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg)); + me->UpdateDamagePhysical(BASE_ATTACK); + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(4000, 7000), 0, PHASE_ONE); + events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE); + events.ScheduleEvent(EVENT_TRANSFORM, urand(16000, 20000), 0, PHASE_ONE); + events.SetPhase(PHASE_ONE); + break; + } default: break; } @@ -198,28 +301,148 @@ class boss_arlokk : public CreatureScript DoMeleeAttackIfReady(); } + + private: + uint8 _summonCountA; + uint8 _summonCountB; + uint64 _triggersSideAGUID[5]; + uint64 _triggersSideBGUID[5]; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetZulGurubAI<boss_arlokkAI>(creature); + } +}; + +/*###### +## npc_zulian_prowler +######*/ + +enum ZulianProwlerSpells +{ + SPELL_SNEAK_RANK_1_1 = 22766, + SPELL_SNEAK_RANK_1_2 = 7939, // Added to Spell_dbc + SPELL_MARK_OF_ARLOKK_TRIGGER = 24211 // Added to Spell_dbc +}; + +enum ZulianProwlerEvents +{ + EVENT_ATTACK = 1 +}; + +Position const PosProwlerCenter[1] = +{ + { -11556.7f, -1631.344f, 41.2994f, 0.0f } +}; + +class npc_zulian_prowler : public CreatureScript +{ + public: npc_zulian_prowler() : CreatureScript("npc_zulian_prowler") {} + + struct npc_zulian_prowlerAI : public ScriptedAI + { + npc_zulian_prowlerAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) { } + + void Reset() + { + if (me->GetPositionY() < -1625.0f) + _sideData = 1; + else + _sideData = 2; + + DoCast(me, SPELL_SNEAK_RANK_1_1); + DoCast(me, SPELL_SNEAK_RANK_1_2); + + if (_instance) + if (Unit* arlokk = me->GetUnit(*me, _instance->GetData64(NPC_ARLOKK))) + me->GetMotionMaster()->MovePoint(0, arlokk->GetPositionX(), arlokk->GetPositionY(), arlokk->GetPositionZ()); + _events.ScheduleEvent(EVENT_ATTACK, 6000); + } + + void EnterCombat(Unit* /*who*/) + { + me->GetMotionMaster()->Clear(false); + me->RemoveAura(SPELL_SNEAK_RANK_1_1); + me->RemoveAura(SPELL_SNEAK_RANK_1_2); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) + { + if (spell->Id == SPELL_MARK_OF_ARLOKK_TRIGGER) // Should only hit if line of sight + me->Attack(caster, true); + } + + void JustDied(Unit* /*killer*/) + { + if (_instance) + { + if (Unit* arlokk = me->GetUnit(*me, _instance->GetData64(NPC_ARLOKK))) + { + if (arlokk->isAlive()) + arlokk->GetAI()->SetData(_sideData, 0); + } + } + me->DespawnOrUnsummon(4000); + } + + void UpdateAI(const uint32 diff) + { + if (UpdateVictim()) + { + DoMeleeAttackIfReady(); + return; + } + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_ATTACK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0.0f, 100, false)) + me->Attack(target, true); + break; + default: + break; + } + } + } + + private: + int32 _sideData; + EventMap _events; + InstanceScript* _instance; }; CreatureAI* GetAI(Creature* creature) const { - return new boss_arlokkAI(creature); + return GetZulGurubAI<npc_zulian_prowlerAI>(creature); } }; +/*###### +## go_gong_of_bethekk +######*/ + +Position const PosSummonArlokk[1] = +{ + { -11507.22f, -1628.062f, 41.38264f, 3.159046f } +}; + class go_gong_of_bethekk : public GameObjectScript { public: go_gong_of_bethekk() : GameObjectScript("go_gong_of_bethekk") {} bool OnGossipHello(Player* /*player*/, GameObject* go) { - if (InstanceScript* instance = go->GetInstanceScript()) + if (go->GetInstanceScript()) { - if (instance->GetData(DATA_ARLOKK) == DONE || instance->GetData(DATA_ARLOKK) == IN_PROGRESS) - return true; - instance->SetData(DATA_ARLOKK, IN_PROGRESS); - return true; + go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + go->SendCustomAnim(0); + go->SummonCreature(NPC_ARLOKK, PosSummonArlokk[0], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 600000); } - return true; } }; @@ -227,6 +450,6 @@ class go_gong_of_bethekk : public GameObjectScript void AddSC_boss_arlokk() { new boss_arlokk(); + new npc_zulian_prowler(); new go_gong_of_bethekk(); } - diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp index 70399b6934c..f3f12bc35c5 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp @@ -112,4 +112,3 @@ void AddSC_boss_gahzranka() { new boss_gahzranka(); } - diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp index 8003f4a1ed7..68aac7547df 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp @@ -90,15 +90,15 @@ class boss_hakkar : public CreatureScript events.ScheduleEvent(EVENT_CAUSE_INSANITY, 17000); events.ScheduleEvent(EVENT_WILL_OF_HAKKAR, 17000); events.ScheduleEvent(EVENT_ENRAGE, 600000); - if (instance->GetData(DATA_JEKLIK) != DONE) + if (instance->GetBossState(DATA_JEKLIK) != DONE) events.ScheduleEvent(EVENT_ASPECT_OF_JEKLIK, 4000); - if (instance->GetData(DATA_VENOXIS) != DONE) + if (instance->GetBossState(DATA_VENOXIS) != DONE) events.ScheduleEvent(EVENT_ASPECT_OF_VENOXIS, 7000); - if (instance->GetData(DATA_MARLI) != DONE) + if (instance->GetBossState(DATA_MARLI) != DONE) events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 12000); - if (instance->GetData(DATA_THEKAL) != DONE) + if (instance->GetBossState(DATA_THEKAL) != DONE) events.ScheduleEvent(EVENT_ASPECT_OF_THEKAL, 8000); - if (instance->GetData(DATA_ARLOKK) != DONE) + if (instance->GetBossState(DATA_ARLOKK) != DONE) events.ScheduleEvent(EVENT_ASPECT_OF_ARLOKK, 18000); Talk(SAY_AGGRO); } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp index 15228815d69..809403bb325 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp @@ -121,4 +121,3 @@ void AddSC_boss_hazzarah() { new boss_hazzarah(); } - diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp index 7f19962e719..dc02c895327 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp @@ -271,7 +271,7 @@ class mob_batrider : public CreatureScript { if (instance) { - if (instance->GetData(DATA_JEKLIK) == DONE) + if (instance->GetBossState(DATA_JEKLIK) == DONE) { me->setDeathState(JUST_DIED); me->RemoveCorpse(); diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp index 258dda26c11..e402480609b 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp @@ -25,6 +25,9 @@ EndScriptData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "Spell.h" +#include "SpellAuras.h" +#include "SpellScript.h" #include "zulgurub.h" enum Says @@ -32,23 +35,73 @@ enum Says SAY_AGGRO = 0, SAY_DING_KILL = 1, SAY_WATCH = 2, - SAY_WATCH_WHISPER = 3, // is this text for real? easter egg? + SAY_WATCH_WHISPER = 3, + SAY_OHGAN_DEAD = 4, SAY_GRATS_JINDO = 0, }; enum Spells { - SPELL_CHARGE = 24408, - SPELL_CLEAVE = 7160, + SPELL_CHARGE = 24408, // seen + SPELL_OVERPOWER = 24407, // Seen SPELL_FEAR = 29321, - SPELL_WHIRLWIND = 15589, - SPELL_MORTAL_STRIKE = 16856, - SPELL_ENRAGE = 24318, - SPELL_WATCH = 24314, - SPELL_LEVEL_UP = 24312, - SPELL_SWIFT_ORANGE_RAPTOR = 23243, - // Ohgans Spell - SPELL_SUNDERARMOR = 24317 + SPELL_WHIRLWIND = 13736, // Triggers 15589 + SPELL_MORTAL_STRIKE = 16856, // Seen + SPELL_FRENZY = 24318, // seen + SPELL_WATCH = 24314, // seen 24315, 24316 + SPELL_WATCH_CHARGE = 24315, // Triggers 24316 + SPELL_LEVEL_UP = 24312 // +}; + +enum Events +{ + EVENT_CHECK_SPEAKER = 1, + EVENT_CHECK_START = 2, + EVENT_STARTED = 3, + EVENT_OVERPOWER = 4, + EVENT_MORTAL_STRIKE = 5, + EVENT_WHIRLWIND = 6, + EVENT_CHECK_OHGAN = 7, + EVENT_WATCH_PLAYER = 8, + EVENT_CHARGE_PLAYER = 9 +}; + +enum Misc +{ + MODEL_OHGAN_MOUNT = 15271, + PATH_MANDOKIR = 492861, + POINT_MANDOKIR_END = 24, + CHAINED_SPIRT_COUNT = 20 +}; + +Position const PosSummonChainedSpirits[CHAINED_SPIRT_COUNT] = +{ + { -12167.17f, -1979.330f, 133.0992f, 2.268928f }, + { -12262.74f, -1953.394f, 133.5496f, 0.593412f }, + { -12176.89f, -1983.068f, 133.7841f, 2.129302f }, + { -12226.45f, -1977.933f, 132.7982f, 1.466077f }, + { -12204.74f, -1890.431f, 135.7569f, 4.415683f }, + { -12216.70f, -1891.806f, 136.3496f, 4.677482f }, + { -12236.19f, -1892.034f, 134.1041f, 5.044002f }, + { -12248.24f, -1893.424f, 134.1182f, 5.270895f }, + { -12257.36f, -1897.663f, 133.1484f, 5.462881f }, + { -12265.84f, -1903.077f, 133.1649f, 5.654867f }, + { -12158.69f, -1972.707f, 133.8751f, 2.408554f }, + { -12178.82f, -1891.974f, 134.1786f, 3.944444f }, + { -12193.36f, -1890.039f, 135.1441f, 4.188790f }, + { -12275.59f, -1932.845f, 134.9017f, 0.174533f }, + { -12273.51f, -1941.539f, 136.1262f, 0.314159f }, + { -12247.02f, -1963.497f, 133.9476f, 0.872665f }, + { -12238.68f, -1969.574f, 133.6273f, 1.134464f }, + { -12192.78f, -1982.116f, 132.6966f, 1.919862f }, + { -12210.81f, -1979.316f, 133.8700f, 1.797689f }, + { -12283.51f, -1924.839f, 133.5170f, 0.069813f } +}; + +Position const PosMandokir[2] = +{ + { -12167.8f, -1927.25f, 153.73f, 3.76991f }, + { -12197.86f, -1949.392f, 130.2745f, 0.0f } }; class boss_mandokir : public CreatureScript @@ -57,298 +110,319 @@ class boss_mandokir : public CreatureScript struct boss_mandokirAI : public BossAI { - boss_mandokirAI(Creature* creature) : BossAI(creature, DATA_MANDOKIR) {} - - uint32 KillCount; - uint32 Watch_Timer; - uint32 TargetInRange; - uint32 Cleave_Timer; - uint32 Whirlwind_Timer; - uint32 Fear_Timer; - uint32 MortalStrike_Timer; - uint32 Check_Timer; - float targetX; - float targetY; - float targetZ; - - InstanceScript* instance; - - bool endWatch; - bool someWatched; - bool RaptorDead; - bool CombatStart; - bool SpeakerDead; - - uint64 WatchTarget; + boss_mandokirAI(Creature* creature) : BossAI(creature, DATA_MANDOKIR) { } void Reset() { - KillCount = 0; - Watch_Timer = 33000; - Cleave_Timer = 7000; - Whirlwind_Timer = 20000; - Fear_Timer = 1000; - MortalStrike_Timer = 1000; - Check_Timer = 1000; - - targetX = 0.0f; - targetY = 0.0f; - targetZ = 0.0f; - TargetInRange = 0; - - WatchTarget = 0; - - someWatched = false; - endWatch = false; - RaptorDead = false; - CombatStart = false; - SpeakerDead = false; - - DoCast(me, 23243); + if (me->GetPositionZ() > 140.0f) + { + _Reset(); + killCount = 0; + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC); + events.ScheduleEvent(EVENT_CHECK_START, 1000); + if (Creature* speaker = Creature::GetCreature(*me, instance->GetData64(NPC_VILEBRANCH_SPEAKER))) + if (!speaker->isAlive()) + speaker->Respawn(true); + } + summons.DespawnAll(); + me->Mount(MODEL_OHGAN_MOUNT); } void JustDied(Unit* /*killer*/) { - _JustDied(); + // Do not want to unsummon Ohgan + for (int i = 0; i < CHAINED_SPIRT_COUNT; ++i) + if (Creature* unsummon = Creature::GetCreature(*me, chainedSpirtGUIDs[i])) + unsummon->DespawnOrUnsummon(); + instance->SetBossState(DATA_MANDOKIR, DONE); + instance->SaveToDB(); } - void KilledUnit(Unit* victim) + void EnterCombat(Unit* /*who*/) { - if (victim->GetTypeId() == TYPEID_PLAYER) + _EnterCombat(); + events.ScheduleEvent(EVENT_OVERPOWER, urand(7000, 9000)); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(12000, 18000)); + events.ScheduleEvent(EVENT_WHIRLWIND, urand(24000, 30000)); + events.ScheduleEvent(EVENT_CHECK_OHGAN, 1000); + events.ScheduleEvent(EVENT_WATCH_PLAYER, urand(13000, 15000)); + events.ScheduleEvent(EVENT_CHARGE_PLAYER, urand(33000, 38000)); + me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation()); + Talk(SAY_AGGRO); + me->Dismount(); + // Summon Ohgan (Spell missing) TEMP HACK + me->SummonCreature(NPC_OHGAN, me->GetPositionX()-3, me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 35000); + // Summon Chained Spirits + for (int i = 0; i < CHAINED_SPIRT_COUNT; ++i) { - ++KillCount; - - if (KillCount == 3) - { - Talk(SAY_DING_KILL); - - if (instance) - { - uint64 JindoGUID = instance->GetData64(DATA_JINDO); - if (JindoGUID) - { - if (Creature* jTemp = Creature::GetCreature(*me, JindoGUID)) - { - if (jTemp->isAlive()) - jTemp->AI()->Talk(SAY_GRATS_JINDO); - } - } - } - DoCast(me, SPELL_LEVEL_UP, true); - KillCount = 0; - } + Creature* chainedSpirt = me->SummonCreature(NPC_CHAINED_SPIRT, PosSummonChainedSpirits[i], TEMPSUMMON_CORPSE_DESPAWN); + chainedSpirtGUIDs[i] = chainedSpirt->GetGUID(); } + DoZoneInCombat(); } - void EnterCombat(Unit* /*who*/) + void KilledUnit(Unit* victim) { - _EnterCombat(); - Talk(SAY_AGGRO); + if (victim->GetTypeId() != TYPEID_PLAYER) + return; + + if (++killCount == 3) + { + Talk(SAY_DING_KILL); + if (Creature* jindo = Creature::GetCreature(*me, instance->GetData64(DATA_JINDO))) + if (jindo->isAlive()) + jindo->AI()->Talk(SAY_GRATS_JINDO); + DoCast(me, SPELL_LEVEL_UP, true); + killCount = 0; + } } - void UpdateAI(const uint32 diff) + void MovementInform(uint32 type, uint32 id) { - if (!SpeakerDead) + if (type == WAYPOINT_MOTION_TYPE) { - if (!me->FindNearestCreature(NPC_SPEAKER, 100.0f, true)) + me->SetWalk(false); + if (id == POINT_MANDOKIR_END) { - me->GetMotionMaster()->MovePoint(0, -12196.3f, -1948.37f, 130.36f); - SpeakerDead = true; + me->SetHomePosition(PosMandokir[0]); + instance->SetBossState(DATA_MANDOKIR, NOT_STARTED); + me->DespawnOrUnsummon(6000); // No idea how to respawn on wipe. } } + } - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE && SpeakerDead) - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + void UpdateAI(uint32 const diff) + { + events.Update(diff); if (!UpdateVictim()) - return; - - if (me->getVictim() && me->isAlive()) { - if (!CombatStart) + if (instance->GetBossState(DATA_MANDOKIR) == NOT_STARTED || instance->GetBossState(DATA_MANDOKIR) == SPECIAL) { - //At combat Start Mandokir is mounted so we must unmount it first - me->Dismount(); - - //And summon his raptor - me->SummonCreature(14988, me->getVictim()->GetPositionX(), me->getVictim()->GetPositionY(), me->getVictim()->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 35000); - CombatStart = true; - } - - if (Watch_Timer <= diff) //Every 20 Sec Mandokir will check this - { - if (WatchTarget) //If someone is watched and If the Position of the watched target is different from the one stored, or are attacking, mandokir will charge him + while (uint32 eventId = events.ExecuteEvent()) { - Unit* unit = Unit::GetUnit(*me, WatchTarget); - - if (unit && ( - targetX != unit->GetPositionX() || - targetY != unit->GetPositionY() || - targetZ != unit->GetPositionZ() || - unit->isInCombat())) + switch (eventId) { - if (me->IsWithinMeleeRange(unit)) - { - DoCast(unit, 24316); - } - else - { - DoCast(unit, SPELL_CHARGE); - //me->SendMonsterMove(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ(), 0, true, 1); - AttackStart(unit); - } + case EVENT_CHECK_START: + if (instance->GetBossState(DATA_MANDOKIR) == SPECIAL) + { + me->GetMotionMaster()->MovePoint(0, PosMandokir[1].m_positionX, PosMandokir[1].m_positionY, PosMandokir[1].m_positionZ); + events.ScheduleEvent(EVENT_STARTED, 6000); + } + else + events.ScheduleEvent(EVENT_CHECK_START, 1000); + break; + case EVENT_STARTED: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_IMMUNE_TO_NPC); + me->GetMotionMaster()->MovePath(PATH_MANDOKIR, false); + break; + default: + break; } } - someWatched = false; - Watch_Timer = 20000; - } else Watch_Timer -= diff; - - if ((Watch_Timer < 8000) && !someWatched) //8 sec(cast time + expire time) before the check for the watch effect mandokir will cast watch debuff on a random target - { - if (Unit* p = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - Talk(SAY_WATCH, p->GetGUID()); - DoCast(p, SPELL_WATCH); - WatchTarget = p->GetGUID(); - someWatched = true; - endWatch = true; - } } + return; + } - if ((Watch_Timer < 1000) && endWatch) //1 sec before the debuf expire, store the target position - { - Unit* unit = Unit::GetUnit(*me, WatchTarget); - if (unit) - { - targetX = unit->GetPositionX(); - targetY = unit->GetPositionY(); - targetZ = unit->GetPositionZ(); - } - endWatch = false; - } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (!someWatched) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - //Cleave - if (Cleave_Timer <= diff) - { - DoCast(me->getVictim(), SPELL_CLEAVE); - Cleave_Timer = 7000; - } else Cleave_Timer -= diff; - - //Whirlwind - if (Whirlwind_Timer <= diff) - { + case EVENT_OVERPOWER: + DoCastVictim(SPELL_OVERPOWER, true); + events.ScheduleEvent(EVENT_OVERPOWER, urand(6000, 12000)); + break; + case EVENT_MORTAL_STRIKE: + if (me->getVictim() && me->getVictim()->HealthBelowPct(50)) + DoCastVictim(SPELL_MORTAL_STRIKE, true); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(12000, 18000)); + break; + case EVENT_WHIRLWIND: DoCast(me, SPELL_WHIRLWIND); - Whirlwind_Timer = 18000; - } else Whirlwind_Timer -= diff; - - //If more then 3 targets in melee range mandokir will cast fear - if (Fear_Timer <= diff) - { - TargetInRange = 0; - - std::list<HostileReference*>::const_iterator i = me->getThreatManager().getThreatList().begin(); - for (; i != me->getThreatManager().getThreatList().end(); ++i) + events.ScheduleEvent(EVENT_WHIRLWIND, urand(22000, 26000)); + break; + case EVENT_CHECK_OHGAN: + if (instance->GetBossState(DATA_OHGAN) == DONE) { - Unit* unit = Unit::GetUnit(*me, (*i)->getUnitGuid()); - if (unit && me->IsWithinMeleeRange(unit)) - ++TargetInRange; + DoCast(me, SPELL_FRENZY); + Talk(SAY_OHGAN_DEAD); } - - if (TargetInRange > 3) - DoCast(me->getVictim(), SPELL_FEAR); - - Fear_Timer = 4000; - } else Fear_Timer -=diff; - - //Mortal Strike if target below 50% hp - if (me->getVictim() && me->getVictim()->HealthBelowPct(50)) - { - if (MortalStrike_Timer <= diff) - { - DoCast(me->getVictim(), SPELL_MORTAL_STRIKE); - MortalStrike_Timer = 15000; - } else MortalStrike_Timer -= diff; - } - } - //Checking if Ohgan is dead. If yes Mandokir will enrage. - if (Check_Timer <= diff) - { - if (instance) - { - if (instance->GetData(DATA_OHGAN) == DONE) + else + events.ScheduleEvent(EVENT_CHECK_OHGAN, 1000); + break; + case EVENT_WATCH_PLAYER: + if (Unit* player = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) { - if (!RaptorDead) - { - DoCast(me, SPELL_ENRAGE); - RaptorDead = true; - } + DoCast(player, SPELL_WATCH); + Talk(SAY_WATCH, player->GetGUID()); } - } - - Check_Timer = 1000; - } else Check_Timer -= diff; - - DoMeleeAttackIfReady(); + events.ScheduleEvent(EVENT_WATCH_PLAYER, urand(12000, 15000)); + break; + case EVENT_CHARGE_PLAYER: + DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0, 40, true), SPELL_CHARGE); + events.ScheduleEvent(EVENT_CHARGE_PLAYER, urand(22000, 30000)); + break; + default: + break; + } } + + DoMeleeAttackIfReady(); } + + private: + uint8 killCount; + uint64 chainedSpirtGUIDs[CHAINED_SPIRT_COUNT]; }; CreatureAI* GetAI(Creature* creature) const { - return new boss_mandokirAI(creature); + return GetZulGurubAI<boss_mandokirAI>(creature); } }; // Ohgan + +enum OhganSpells +{ + SPELL_SUNDERARMOR = 24317 +}; + class mob_ohgan : public CreatureScript { public: mob_ohgan() : CreatureScript("mob_ohgan") {} struct mob_ohganAI : public ScriptedAI { - mob_ohganAI(Creature* creature) : ScriptedAI(creature) + mob_ohganAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()) { } + + void Reset() { - instance = creature->GetInstanceScript(); + SunderArmor_Timer = 5000; } + void EnterCombat(Unit* /*who*/) {} + + void JustDied(Unit* /*killer*/) + { + instance->SetBossState(DATA_OHGAN, DONE); + } + + void UpdateAI(const uint32 diff) + { + // Return since we have no target + if (!UpdateVictim()) + return; + + if (SunderArmor_Timer <= diff) + { + DoCastVictim(SPELL_SUNDERARMOR, true); + SunderArmor_Timer = urand(10000, 15000); + } else SunderArmor_Timer -= diff; + + DoMeleeAttackIfReady(); + } + + private: uint32 SunderArmor_Timer; InstanceScript* instance; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetZulGurubAI<mob_ohganAI>(creature); + } +}; + +enum VilebranchSpells +{ + SPELL_DEMORALIZING_SHOUT = 13730, + SPELL_CLEAVE = 15284 +}; + +class mob_vilebranch_speaker : public CreatureScript +{ + public: mob_vilebranch_speaker() : CreatureScript("mob_vilebranch_speaker") {} + + struct mob_vilebranch_speakerAI : public ScriptedAI + { + mob_vilebranch_speakerAI(Creature* creature) : ScriptedAI(creature), instance(creature->GetInstanceScript()) { } void Reset() { - SunderArmor_Timer = 5000; + demoralizing_Shout_Timer = urand (2000, 4000); + cleave_Timer = urand (5000, 8000); } void EnterCombat(Unit* /*who*/) {} void JustDied(Unit* /*killer*/) { - if (instance) - instance->SetData(DATA_OHGAN, DONE); + instance->SetBossState(DATA_MANDOKIR, SPECIAL); } - void UpdateAI (const uint32 diff) + void UpdateAI(const uint32 diff) { - //Return since we have no target + // Return since we have no target if (!UpdateVictim()) return; - //SunderArmor_Timer - if (SunderArmor_Timer <= diff) + if (demoralizing_Shout_Timer <= diff) { - DoCast(me->getVictim(), SPELL_SUNDERARMOR); - SunderArmor_Timer = urand(10000, 15000); - } else SunderArmor_Timer -= diff; + DoCast(me, SPELL_DEMORALIZING_SHOUT); + demoralizing_Shout_Timer = urand(22000, 30000); + } else demoralizing_Shout_Timer -= diff; + + if (cleave_Timer <= diff) + { + DoCastVictim(SPELL_CLEAVE, true); + cleave_Timer = urand(6000, 9000); + } else cleave_Timer -= diff; DoMeleeAttackIfReady(); } + + private: + uint32 demoralizing_Shout_Timer; + uint32 cleave_Timer; + InstanceScript* instance; }; CreatureAI* GetAI(Creature* creature) const { - return new mob_ohganAI(creature); + return new mob_vilebranch_speakerAI(creature); + } +}; + +class spell_threatening_gaze : public SpellScriptLoader +{ + public: + spell_threatening_gaze() : SpellScriptLoader("spell_threatening_gaze") { } + + class spell_threatening_gaze_AuraScript : public AuraScript + { + PrepareAuraScript(spell_threatening_gaze_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if(Unit* target = GetTarget()) + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE && GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEATH) + caster->CastSpell(target, SPELL_WATCH_CHARGE); + } + + void Register() + { + OnEffectRemove += AuraEffectRemoveFn(spell_threatening_gaze_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_threatening_gaze_AuraScript(); } }; @@ -356,5 +430,6 @@ void AddSC_boss_mandokir() { new boss_mandokir(); new mob_ohgan(); + new mob_vilebranch_speaker(); + new spell_threatening_gaze(); } - diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp index ab45162fc35..61eb695719a 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_thekal.cpp @@ -113,7 +113,7 @@ class boss_thekal : public CreatureScript void JustReachedHome() { if (instance) - instance->SetData(DATA_THEKAL, NOT_STARTED); + instance->SetBossState(DATA_THEKAL, NOT_STARTED); } void UpdateAI(uint32 const diff) @@ -167,7 +167,7 @@ class boss_thekal : public CreatureScript { if (instance) { - if (instance->GetData(DATA_LORKHAN) == SPECIAL) + if (instance->GetBossState(DATA_LORKHAN) == SPECIAL) { //Resurrect LorKhan if (Unit* pLorKhan = Unit::GetUnit(*me, instance->GetData64(DATA_LORKHAN))) @@ -180,7 +180,7 @@ class boss_thekal : public CreatureScript } } - if (instance->GetData(DATA_ZATH) == SPECIAL) + if (instance->GetBossState(DATA_ZATH) == SPECIAL) { //Resurrect Zath if (Unit* pZath = Unit::GetUnit(*me, instance->GetData64(DATA_ZATH))) @@ -189,7 +189,7 @@ class boss_thekal : public CreatureScript pZath->setFaction(14); pZath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); pZath->SetFullHealth(); - instance->SetData(DATA_ZATH, DONE); + instance->SetBossState(DATA_ZATH, DONE); } } } @@ -241,7 +241,7 @@ class boss_thekal : public CreatureScript me->SetStandState(UNIT_STAND_STATE_SLEEP); me->AttackStop(); if (instance) - instance->SetData(DATA_THEKAL, SPECIAL); + instance->SetBossState(DATA_THEKAL, SPECIAL); WasDead=true; } } @@ -288,7 +288,7 @@ class mob_zealot_lorkhan : public CreatureScript FakeDeath = false; if (instance) - instance->SetData(DATA_LORKHAN, NOT_STARTED); + instance->SetBossState(DATA_LORKHAN, NOT_STARTED); me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); @@ -356,7 +356,7 @@ class mob_zealot_lorkhan : public CreatureScript { if (instance) { - if (instance->GetData(DATA_THEKAL) == SPECIAL) + if (instance->GetBossState(DATA_THEKAL) == SPECIAL) { //Resurrect Thekal if (Unit* pThekal = Unit::GetUnit(*me, instance->GetData64(DATA_THEKAL))) @@ -368,7 +368,7 @@ class mob_zealot_lorkhan : public CreatureScript } } - if (instance->GetData(DATA_ZATH) == SPECIAL) + if (instance->GetBossState(DATA_ZATH) == SPECIAL) { //Resurrect Zath if (Unit* pZath = Unit::GetUnit(*me, instance->GetData64(DATA_ZATH))) @@ -395,7 +395,7 @@ class mob_zealot_lorkhan : public CreatureScript me->AttackStop(); if (instance) - instance->SetData(DATA_LORKHAN, SPECIAL); + instance->SetBossState(DATA_LORKHAN, SPECIAL); FakeDeath = true; } @@ -450,7 +450,7 @@ class mob_zealot_zath : public CreatureScript FakeDeath = false; if (instance) - instance->SetData(DATA_ZATH, NOT_STARTED); + instance->SetBossState(DATA_ZATH, NOT_STARTED); me->SetStandState(UNIT_STAND_STATE_STAND); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); @@ -509,7 +509,7 @@ class mob_zealot_zath : public CreatureScript { if (instance) { - if (instance->GetData(DATA_LORKHAN) == SPECIAL) + if (instance->GetBossState(DATA_LORKHAN) == SPECIAL) { //Resurrect LorKhan if (Unit* pLorKhan = Unit::GetUnit(*me, instance->GetData64(DATA_LORKHAN))) @@ -521,7 +521,7 @@ class mob_zealot_zath : public CreatureScript } } - if (instance->GetData(DATA_THEKAL) == SPECIAL) + if (instance->GetBossState(DATA_THEKAL) == SPECIAL) { //Resurrect Thekal if (Unit* pThekal = Unit::GetUnit(*me, instance->GetData64(DATA_THEKAL))) @@ -548,7 +548,7 @@ class mob_zealot_zath : public CreatureScript me->AttackStop(); if (instance) - instance->SetData(DATA_ZATH, SPECIAL); + instance->SetBossState(DATA_ZATH, SPECIAL); FakeDeath = true; } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp index b997d277c95..01c5ef998f5 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/instance_zulgurub.cpp @@ -29,7 +29,7 @@ EndScriptData */ class instance_zulgurub : public InstanceMapScript { - public: instance_zulgurub(): InstanceMapScript("instance_zulgurub", 309) {} + public: instance_zulgurub(): InstanceMapScript(ZGScriptName, 309) {} struct instance_zulgurub_InstanceMapScript : public InstanceScript { @@ -38,24 +38,21 @@ class instance_zulgurub : public InstanceMapScript SetBossNumber(EncounterCount); } - //If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too. - //Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too. - uint64 LorKhanGUID; - uint64 ZathGUID; - uint64 ThekalGUID; - uint64 JindoGUID; - void Initialize() { - LorKhanGUID = 0; - ZathGUID = 0; - ThekalGUID = 0; - JindoGUID = 0; + _zealotLorkhanGUID = 0; + _zealotZathGUID = 0; + _highPriestTekalGUID = 0; + _jindoTheHexxerGUID = 0; + _vilebranchSpeakerGUID = 0; + _arlokkGUID = 0; + _goForcefieldGUID = 0; + _goGongOfBethekkGUID = 0; } bool IsEncounterInProgress() const { - //not active in Zul'Gurub + // not active in Zul'Gurub return false; } @@ -63,78 +60,71 @@ class instance_zulgurub : public InstanceMapScript { switch (creature->GetEntry()) { - case NPC_ZEALOT_LORKHAN: LorKhanGUID = creature->GetGUID(); break; - case NPC_ZEALOT_ZATH: ZathGUID = creature->GetGUID(); break; - case NPC_HIGH_PRIEST_THEKAL: ThekalGUID = creature->GetGUID(); break; - case NPC_JINDO_THE_HEXXER: JindoGUID = creature->GetGUID(); break; - } - } - - bool SetBossState(uint32 type, EncounterState state) - { - if (!InstanceScript::SetBossState(type, state)) - return false; - - switch (type) - { - case DATA_JEKLIK: - case DATA_VENOXIS: - case DATA_MARLI: - case DATA_ARLOKK: - case DATA_HAKKAR: - case DATA_MANDOKIR: - case DATA_JINDO: - case DATA_GAHZRANKA: - case DATA_EDGE_OF_MADNESS: - case DATA_THEKAL: - case DATA_LORKHAN: - case DATA_ZATH: - case DATA_OHGAN: + case NPC_ZEALOT_LORKHAN: + _zealotLorkhanGUID = creature->GetGUID(); break; - default: + case NPC_ZEALOT_ZATH: + _zealotZathGUID = creature->GetGUID(); + break; + case NPC_HIGH_PRIEST_THEKAL: + _highPriestTekalGUID = creature->GetGUID(); + break; + case NPC_JINDO_THE_HEXXER: + _jindoTheHexxerGUID = creature->GetGUID(); + break; + case NPC_VILEBRANCH_SPEAKER: + _vilebranchSpeakerGUID = creature->GetGUID(); + break; + case NPC_ARLOKK: + _arlokkGUID = creature->GetGUID(); break; } - - return true; } - uint32 GetData(uint32 type) const - { - switch (type) + void OnGameObjectCreate(GameObject* go) { - case DATA_JEKLIK: return GetBossState(DATA_JEKLIK); - case DATA_VENOXIS: return GetBossState(DATA_VENOXIS); - case DATA_MARLI: return GetBossState(DATA_MARLI); - case DATA_ARLOKK: return GetBossState(DATA_ARLOKK); - case DATA_HAKKAR: return GetBossState(DATA_HAKKAR); - case DATA_MANDOKIR: return GetBossState(DATA_MANDOKIR); - case DATA_JINDO: return GetBossState(DATA_JINDO); - case DATA_GAHZRANKA: return GetBossState(DATA_GAHZRANKA); - case DATA_EDGE_OF_MADNESS: return GetBossState(DATA_EDGE_OF_MADNESS); - case DATA_THEKAL: return GetBossState(DATA_THEKAL); - case DATA_LORKHAN: return GetBossState(DATA_LORKHAN); - case DATA_ZATH: return GetBossState(DATA_ZATH); - case DATA_OHGAN: return GetBossState(DATA_OHGAN); + switch (go->GetEntry()) + { + case GO_FORCEFIELD: + _goForcefieldGUID = go->GetGUID(); + break; + case GO_GONG_OF_BETHEKK: + _goGongOfBethekkGUID = go->GetGUID(); + if (GetBossState(DATA_ARLOKK) == DONE) + go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); + else + go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); break; default: break; + } } - return 0; - } - uint64 GetData64(uint32 uiData) const { switch (uiData) { case DATA_LORKHAN: - return LorKhanGUID; + return _zealotLorkhanGUID; + break; case DATA_ZATH: - return ZathGUID; + return _zealotZathGUID; + break; case DATA_THEKAL: - return ThekalGUID; + return _highPriestTekalGUID; + break; case DATA_JINDO: - return JindoGUID; + return _jindoTheHexxerGUID; + break; + case NPC_ARLOKK: + return _arlokkGUID; + break; + case GO_FORCEFIELD: + return _goForcefieldGUID; + break; + case GO_GONG_OF_BETHEKK: + return _goGongOfBethekkGUID; + break; } return 0; } @@ -181,6 +171,18 @@ class instance_zulgurub : public InstanceMapScript OUT_LOAD_INST_DATA_COMPLETE; } + private: + //If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too. + //Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too. + + uint64 _zealotLorkhanGUID; + uint64 _zealotZathGUID; + uint64 _highPriestTekalGUID; + uint64 _jindoTheHexxerGUID; + uint64 _vilebranchSpeakerGUID; + uint64 _arlokkGUID; + uint64 _goForcefieldGUID; + uint64 _goGongOfBethekkGUID; }; InstanceScript* GetInstanceScript(InstanceMap* map) const diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h index 2544e4f4d5f..77767153a96 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h +++ b/src/server/scripts/EasternKingdoms/ZulGurub/zulgurub.h @@ -20,6 +20,8 @@ uint32 const EncounterCount = 13; +#define ZGScriptName "instance_zulgurub" + enum DataTypes { DATA_JEKLIK = 0, // Main boss @@ -34,20 +36,43 @@ enum DataTypes DATA_EDGE_OF_MADNESS = 9, // Optional Event Edge of Madness - one of: Gri'lek, Renataki, Hazza'rah, or Wushoolay DATA_LORKHAN = 10, // Zealot Lor'Khan add to High priest Thekal! DATA_ZATH = 11, // Zealot Zath add to High priest Thekal! - DATA_OHGAN = 12 // Bloodlord Mandokir's raptor mount + DATA_OHGAN = 12, // Bloodlord Mandokir's raptor mount + TYPE_EDGE_OF_MADNESS = 13 // Boss storage }; enum CreatureIds { + NPC_ARLOKK = 14515, // Arlokk Event + NPC_PANTHER_TRIGGER = 15091, // Arlokk Event + NPC_ZULIAN_PROWLER = 15101, // Arlokk Event NPC_ZEALOT_LORKHAN = 11347, NPC_ZEALOT_ZATH = 11348, NPC_HIGH_PRIEST_THEKAL = 14509, NPC_JINDO_THE_HEXXER = 11380, NPC_NIGHTMARE_ILLUSION = 15163, - NPC_ZULIAN_PROWLER = 15101, - NPC_SPEAKER = 11391, NPC_SHADE_OF_JINDO = 14986, - NPC_SACRIFICED_TROLL = 14826 + NPC_SACRIFICED_TROLL = 14826, + NPC_MANDOKIR = 11382, // Mandokir Event + NPC_OHGAN = 14988, // Mandokir Event + NPC_VILEBRANCH_SPEAKER = 11391, // Mandokir Event + NPC_CHAINED_SPIRT = 15117 // Mandokir Event + }; +enum GameobjectIds +{ + GO_FORCEFIELD = 180497, // Arlokk Event + GO_GONG_OF_BETHEKK = 180526 // Arlokk Event +}; + +template<class AI> +CreatureAI* GetZulGurubAI(Creature* creature) +{ + if (InstanceMap* instance = creature->GetMap()->ToInstanceMap()) + if (instance->GetInstanceScript()) + if (instance->GetScriptId() == sObjectMgr->GetScriptId(ZGScriptName)) + return new AI(creature); + return NULL; +} + #endif diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index 7f8108e5e71..6039e01b901 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -359,7 +359,7 @@ class boss_halion : public CreatureScript if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_TWILIGHT_HALION))) if (twilightHalion->isAlive()) twilightHalion->Kill(twilightHalion); - + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) if (controller->isAlive()) controller->Kill(controller); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp index c8caa3976e4..ab0c44aa6d0 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp @@ -46,6 +46,7 @@ enum Spells SPELL_FRENZIED_BLOODTHIRST_VISUAL = 71949, SPELL_VAMPIRIC_BITE = 71726, SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_PLR = 70879, + SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL = 70872, SPELL_FRENZIED_BLOODTHIRST = 70877, SPELL_UNCONTROLLABLE_FRENZY = 70923, SPELL_PRESENCE_OF_THE_DARKFALLEN = 71952, @@ -698,6 +699,42 @@ class spell_blood_queen_bloodbolt : public SpellScriptLoader } }; +// 70871 - Essence of the Blood Queen +class spell_blood_queen_essence_of_the_blood_queen : public SpellScriptLoader +{ + public: + spell_blood_queen_essence_of_the_blood_queen() : SpellScriptLoader("spell_blood_queen_essence_of_the_blood_queen") { } + + class spell_blood_queen_essence_of_the_blood_queen_AuraScript : public AuraScript + { + PrepareAuraScript(spell_blood_queen_essence_of_the_blood_queen_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL)) + return false; + return true; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 heal = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), TRIGGERED_FULL_MASK, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_blood_queen_essence_of_the_blood_queen_AuraScript::OnProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_blood_queen_essence_of_the_blood_queen_AuraScript(); + } +}; + class spell_blood_queen_pact_of_the_darkfallen : public SpellScriptLoader { public: @@ -849,6 +886,7 @@ void AddSC_boss_blood_queen_lana_thel() new spell_blood_queen_vampiric_bite(); new spell_blood_queen_frenzied_bloodthirst(); new spell_blood_queen_bloodbolt(); + new spell_blood_queen_essence_of_the_blood_queen(); new spell_blood_queen_pact_of_the_darkfallen(); new spell_blood_queen_pact_of_the_darkfallen_dmg(); new spell_blood_queen_pact_of_the_darkfallen_dmg_target(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 452d09cf65f..3bd313fa148 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -145,6 +145,7 @@ enum Spells SPELL_RESTORE_SOUL = 72595, SPELL_RESTORE_SOULS = 73650, // Heroic SPELL_DARK_HUNGER = 69383, // Passive proc healing + SPELL_DARK_HUNGER_HEAL = 69384, SPELL_DESTROY_SOUL = 74086, // Used when Terenas Menethil dies SPELL_SOUL_RIP = 69397, // Deals increasing damage SPELL_SOUL_RIP_DAMAGE = 69398, @@ -3008,6 +3009,41 @@ class spell_the_lich_king_restore_soul : public SpellScriptLoader } }; +class spell_the_lich_king_dark_hunger : public SpellScriptLoader +{ + public: + spell_the_lich_king_dark_hunger() : SpellScriptLoader("spell_the_lich_king_dark_hunger") { } + + class spell_the_lich_king_dark_hunger_AuraScript : public AuraScript + { + PrepareAuraScript(spell_the_lich_king_dark_hunger_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DARK_HUNGER_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 heal = int32(eventInfo.GetDamageInfo()->GetDamage() / 2); + GetTarget()->CastCustomSpell(SPELL_DARK_HUNGER_HEAL, SPELLVALUE_BASE_POINT0, heal, GetTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_the_lich_king_dark_hunger_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_the_lich_king_dark_hunger_AuraScript(); + } +}; + class spell_the_lich_king_in_frostmourne_room : public SpellScriptLoader { public: @@ -3253,6 +3289,7 @@ void AddSC_boss_the_lich_king() new spell_the_lich_king_lights_favor(); new spell_the_lich_king_soul_rip(); new spell_the_lich_king_restore_soul(); + new spell_the_lich_king_dark_hunger(); new spell_the_lich_king_in_frostmourne_room(); new spell_the_lich_king_summon_spirit_bomb(); new spell_the_lich_king_trigger_vile_spirit(); 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 1f96848fa0a..13a4fe23690 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 @@ -343,7 +343,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/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index f3afc03e68a..7db2ae4707b 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -1033,6 +1033,102 @@ public: } }; +/*###### +## Quest: Reconnaissance Flight (12671) +######*/ +enum ReconnaissanceFlight +{ + NPC_PLANE = 28710, // Vic's Flying Machine + NPC_PILOT = 28646, + + VIC_SAY_0 = 0, + VIC_SAY_1 = 1, + VIC_SAY_2 = 2, + VIC_SAY_3 = 3, + VIC_SAY_4 = 4, + VIC_SAY_5 = 5, + VIC_SAY_6 = 6, + PLANE_EMOTE = 0, + + AURA_ENGINE = 52255, // Engine on Fire + + SPELL_LAND = 52226, // Land Flying Machine + SPELL_CREDIT = 53328 // Land Flying Machine Credit +}; + +class npc_vics_flying_machine : public CreatureScript +{ +public: + npc_vics_flying_machine() : CreatureScript("npc_vics_flying_machine") { } + + struct npc_vics_flying_machineAI : public VehicleAI + { + npc_vics_flying_machineAI(Creature* creature) : VehicleAI(creature) {} + + void PassengerBoarded(Unit* passenger, int8 /*seatId*/, bool apply) + { + if (apply && passenger->GetTypeId() == TYPEID_PLAYER) + me->GetMotionMaster()->MovePath(NPC_PLANE, false); + } + + void MovementInform(uint32 type, uint32 id) + { + if (type != WAYPOINT_MOTION_TYPE) + return; + + if (Creature* pilot = GetClosestCreatureWithEntry(me, NPC_PILOT, 10)) + switch (id) + { + case 5: + pilot->AI()->Talk(VIC_SAY_0); + break; + case 11: + pilot->AI()->Talk(VIC_SAY_1); + break; + case 12: + pilot->AI()->Talk(VIC_SAY_2); + break; + case 14: + pilot->AI()->Talk(VIC_SAY_3); + break; + case 15: + pilot->AI()->Talk(VIC_SAY_4); + break; + case 17: + pilot->AI()->Talk(VIC_SAY_5); + break; + case 21: + pilot->AI()->Talk(VIC_SAY_6); + break; + case 25: + me->AI()->Talk(PLANE_EMOTE); + me->AI()->DoCast(AURA_ENGINE); + break; + } + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) + { + if (spell->Id == SPELL_LAND) + { + Unit* passenger = me->GetVehicleKit()->GetPassenger(1); // player should be on seat 1 + if (passenger && passenger->GetTypeId() == TYPEID_PLAYER) + { + passenger->CastSpell(passenger, SPELL_CREDIT, true); + passenger->ExitVehicle(); + } + } + } + + void UpdateAI(const uint32 /*diff*/) {} + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_vics_flying_machineAI(creature); + } +}; + void AddSC_sholazar_basin() { new npc_injured_rainspeaker_oracle(); @@ -1045,4 +1141,5 @@ void AddSC_sholazar_basin() new spell_q12620_the_lifewarden_wrath(); new spell_q12589_shoot_rjr(); new npc_haiphoon(); + new npc_vics_flying_machine(); } diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 060db02d53c..73c34d42538 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -31,12 +31,14 @@ enum DeathKnightSpells SPELL_DK_ANTI_MAGIC_SHELL_TALENT = 51052, SPELL_DK_BLACK_ICE_R1 = 49140, SPELL_DK_BLOOD_BOIL_TRIGGERED = 65658, + SPELL_DK_BLOOD_GORGED_HEAL = 50454, SPELL_DK_CORPSE_EXPLOSION_TRIGGERED = 43999, SPELL_DK_CORPSE_EXPLOSION_VISUAL = 51270, SPELL_DK_DEATH_COIL_DAMAGE = 47632, SPELL_DK_DEATH_COIL_HEAL = 47633, SPELL_DK_DEATH_STRIKE_HEAL = 45470, SPELL_DK_GHOUL_EXPLODE = 47496, + SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE = 58625, SPELL_DK_RUNIC_POWER_ENERGIZE = 49088, SPELL_DK_SCOURGE_STRIKE_TRIGGERED = 70890, SPELL_DK_WILL_OF_THE_NECROPOLIS_TALENT_R1 = 49189, @@ -251,6 +253,58 @@ class spell_dk_blood_boil : public SpellScriptLoader } }; +// 50453 - Bloodworms Health Leech +class spell_dk_blood_gorged : public SpellScriptLoader +{ + public: + spell_dk_blood_gorged() : SpellScriptLoader("spell_dk_blood_gorged") { } + + class spell_dk_blood_gorged_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_blood_gorged_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_BLOOD_GORGED_HEAL)) + return false; + return true; + } + + bool Load() + { + _procTarget = NULL; + return true; + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + _procTarget = GetTarget()->GetOwner(); + return _procTarget; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 bp = int32(eventInfo.GetDamageInfo()->GetDamage() * 1.5f); + GetTarget()->CastCustomSpell(SPELL_DK_BLOOD_GORGED_HEAL, SPELLVALUE_BASE_POINT0, bp, _procTarget, true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_dk_blood_gorged_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_blood_gorged_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + private: + Unit* _procTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_dk_blood_gorged_AuraScript(); + } +}; + // 49158 - Corpse Explosion (51325, 51326, 51327, 51328) class spell_dk_corpse_explosion : public SpellScriptLoader { @@ -584,6 +638,55 @@ class spell_dk_ghoul_explode : public SpellScriptLoader } }; +// 48792 - Icebound Fortitude +class spell_dk_icebound_fortitude : public SpellScriptLoader +{ + public: + spell_dk_icebound_fortitude() : SpellScriptLoader("spell_dk_icebound_fortitude") { } + + class spell_dk_icebound_fortitude_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_icebound_fortitude_AuraScript); + + bool Load() + { + Unit* caster = GetCaster(); + return caster && caster->GetTypeId() == TYPEID_PLAYER; + } + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* caster = GetCaster()) + { + int32 value = amount; + uint32 defValue = uint32(caster->ToPlayer()->GetSkillValue(SKILL_DEFENSE) + caster->ToPlayer()->GetRatingBonusValue(CR_DEFENSE_SKILL)); + + if (defValue > 400) + value -= int32((defValue - 400) * 0.15); + + // Glyph of Icebound Fortitude + if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE, EFFECT_0)) + { + int32 valMax = -aurEff->GetAmount(); + if (value > valMax) + value = valMax; + } + amount = value; + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dk_icebound_fortitude_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dk_icebound_fortitude_AuraScript(); + } +}; + // 50365, 50371 - Improved Blood Presence class spell_dk_improved_blood_presence : public SpellScriptLoader { @@ -677,6 +780,33 @@ class spell_dk_improved_unholy_presence : public SpellScriptLoader } }; +// 59754 Rune Tap - Party +class spell_dk_rune_tap_party : public SpellScriptLoader +{ + public: + spell_dk_rune_tap_party() : SpellScriptLoader("spell_dk_rune_tap_party") { } + + class spell_dk_rune_tap_party_SpellScript : public SpellScript + { + PrepareSpellScript(spell_dk_rune_tap_party_SpellScript); + + void CheckTargets(std::list<WorldObject*>& targets) + { + targets.remove(GetCaster()); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_dk_rune_tap_party_SpellScript::CheckTargets, EFFECT_0, TARGET_UNIT_CASTER_AREA_PARTY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_dk_rune_tap_party_SpellScript(); + } +}; + // 55090 - Scourge Strike (55265, 55270, 55271) class spell_dk_scourge_strike : public SpellScriptLoader { @@ -784,6 +914,33 @@ class spell_dk_spell_deflection : public SpellScriptLoader } }; +// 55233 - Vampiric Blood +class spell_dk_vampiric_blood : public SpellScriptLoader +{ + public: + spell_dk_vampiric_blood() : SpellScriptLoader("spell_dk_vampiric_blood") { } + + class spell_dk_vampiric_blood_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_vampiric_blood_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + amount = GetUnitOwner()->CountPctFromMaxHealth(amount); + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dk_vampiric_blood_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_INCREASE_HEALTH); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dk_vampiric_blood_AuraScript(); + } +}; + // 52284 - Will of the Necropolis class spell_dk_will_of_the_necropolis : public SpellScriptLoader { @@ -854,6 +1011,7 @@ void AddSC_deathknight_spell_scripts() new spell_dk_anti_magic_shell_self(); new spell_dk_anti_magic_zone(); new spell_dk_blood_boil(); + new spell_dk_blood_gorged(); new spell_dk_corpse_explosion(); new spell_dk_death_coil(); new spell_dk_death_gate(); @@ -861,9 +1019,12 @@ void AddSC_deathknight_spell_scripts() new spell_dk_death_pact(); new spell_dk_death_strike(); new spell_dk_ghoul_explode(); + new spell_dk_icebound_fortitude(); new spell_dk_improved_blood_presence(); new spell_dk_improved_unholy_presence(); + new spell_dk_rune_tap_party(); new spell_dk_scourge_strike(); new spell_dk_spell_deflection(); + new spell_dk_vampiric_blood(); new spell_dk_will_of_the_necropolis(); } diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 6dd453597de..160f14ff670 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -30,10 +30,15 @@ enum DruidSpells { SPELL_DRUID_ENRAGE_MOD_DAMAGE = 51185, + SPELL_DRUID_GLYPH_OF_TYPHOON = 62135, + SPELL_DRUID_IDOL_OF_FERAL_SHADOWS = 34241, + SPELL_DRUID_IDOL_OF_WORSHIP = 60774, SPELL_DRUID_INCREASED_MOONFIRE_DURATION = 38414, SPELL_DRUID_KING_OF_THE_JUNGLE = 48492, SPELL_DRUID_LIFEBLOOM_ENERGIZE = 64372, SPELL_DRUID_LIFEBLOOM_FINAL_HEAL = 33778, + SPELL_DRUID_LIVING_SEED_HEAL = 48503, + SPELL_DRUID_LIVING_SEED_PROC = 48504, SPELL_DRUID_NATURES_SPLENDOR = 57865, SPELL_DRUID_SURVIVAL_INSTINCTS = 50322, SPELL_DRUID_SAVAGE_ROAR = 62071, @@ -41,6 +46,35 @@ enum DruidSpells SPELL_DRUID_ITEM_T8_BALANCE_RELIC = 64950, }; +// -1850 - Dash +class spell_dru_dash : public SpellScriptLoader +{ + public: + spell_dru_dash() : SpellScriptLoader("spell_dru_dash") { } + + class spell_dru_dash_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_dash_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + // do not set speed if not in cat form + if (GetUnitOwner()->GetShapeshiftForm() != FORM_CAT) + amount = 0; + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_dash_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_INCREASE_SPEED); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_dash_AuraScript(); + } +}; + // -5229 - Enrage class spell_dru_enrage : public SpellScriptLoader { @@ -121,6 +155,69 @@ class spell_dru_glyph_of_starfire : public SpellScriptLoader } }; +// 34246 - Idol of the Emerald Queen +// 60779 - Idol of Lush Moss +class spell_dru_idol_lifebloom : public SpellScriptLoader +{ + public: + spell_dru_idol_lifebloom() : SpellScriptLoader("spell_dru_idol_lifebloom") { } + + class spell_dru_idol_lifebloom_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_idol_lifebloom_AuraScript); + + void HandleEffectCalcSpellMod(AuraEffect const* aurEff, SpellModifier*& spellMod) + { + if (!spellMod) + { + spellMod = new SpellModifier(GetAura()); + spellMod->op = SPELLMOD_DOT; + spellMod->type = SPELLMOD_FLAT; + spellMod->spellId = GetId(); + spellMod->mask = GetSpellInfo()->Effects[aurEff->GetEffIndex()].SpellClassMask; + } + spellMod->value = aurEff->GetAmount() / 7; + } + + void Register() + { + DoEffectCalcSpellMod += AuraEffectCalcSpellModFn(spell_dru_idol_lifebloom_AuraScript::HandleEffectCalcSpellMod, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_idol_lifebloom_AuraScript(); + } +}; + +// 29166 - Innervate +class spell_dru_innervate : public SpellScriptLoader +{ + public: + spell_dru_innervate() : SpellScriptLoader("spell_dru_innervate") { } + + class spell_dru_innervate_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_innervate_AuraScript); + + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + amount = CalculatePct(int32(GetUnitOwner()->GetCreatePowers(POWER_MANA) / aurEff->GetTotalTicks()), amount); + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_innervate_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_ENERGIZE); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_innervate_AuraScript(); + } +}; + // -5570 - Insect Swarm class spell_dru_insect_swarm : public SpellScriptLoader { @@ -232,6 +329,77 @@ class spell_dru_lifebloom : public SpellScriptLoader } }; +// -48496 - Living Seed +class spell_dru_living_seed : public SpellScriptLoader +{ + public: + spell_dru_living_seed() : SpellScriptLoader("spell_dru_living_seed") { } + + class spell_dru_living_seed_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_living_seed_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_LIVING_SEED_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 amount = CalculatePct(eventInfo.GetHealInfo()->GetHeal(), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_DRUID_LIVING_SEED_PROC, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_dru_living_seed_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_living_seed_AuraScript(); + } +}; + +// 48504 - Living Seed (Proc) +class spell_dru_living_seed_proc : public SpellScriptLoader +{ + public: + spell_dru_living_seed_proc() : SpellScriptLoader("spell_dru_living_seed_proc") { } + + class spell_dru_living_seed_proc_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_living_seed_proc_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_LIVING_SEED_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastCustomSpell(SPELL_DRUID_LIVING_SEED_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), GetTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_dru_living_seed_proc_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_living_seed_proc_AuraScript(); + } +}; + // 69366 - Moonkin Form passive class spell_dru_moonkin_form_passive : public SpellScriptLoader { @@ -276,6 +444,33 @@ class spell_dru_moonkin_form_passive : public SpellScriptLoader } }; +// 48391 - Owlkin Frenzy +class spell_dru_owlkin_frenzy : public SpellScriptLoader +{ + public: + spell_dru_owlkin_frenzy() : SpellScriptLoader("spell_dru_owlkin_frenzy") { } + + class spell_dru_owlkin_frenzy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_owlkin_frenzy_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + amount = CalculatePct(GetUnitOwner()->GetCreatePowers(POWER_MANA), amount); + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_owlkin_frenzy_AuraScript::CalculateAmount, EFFECT_2, SPELL_AURA_PERIODIC_ENERGIZE); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_owlkin_frenzy_AuraScript(); + } +}; + // -16972 - Predatory Strikes class spell_dru_predatory_strikes : public SpellScriptLoader { @@ -349,6 +544,54 @@ class spell_dru_primal_tenacity : public SpellScriptLoader } }; +// -1079 - Rip +class spell_dru_rip : public SpellScriptLoader +{ + public: + spell_dru_rip() : SpellScriptLoader("spell_dru_rip") { } + + class spell_dru_rip_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_rip_AuraScript); + + bool Load() + { + Unit* caster = GetCaster(); + return caster && caster->GetTypeId() == TYPEID_PLAYER; + } + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated) + { + canBeRecalculated = false; + + if (Unit* caster = GetCaster()) + { + // 0.01 * $AP * cp + uint8 cp = caster->ToPlayer()->GetComboPoints(); + + // Idol of Feral Shadows. Can't be handled as SpellMod due its dependency from CPs + if (AuraEffect const* idol = caster->GetAuraEffect(SPELL_DRUID_IDOL_OF_FERAL_SHADOWS, EFFECT_0)) + amount += cp * idol->GetAmount(); + // Idol of Worship. Can't be handled as SpellMod due its dependency from CPs + else if (AuraEffect const* idol = caster->GetAuraEffect(SPELL_DRUID_IDOL_OF_WORSHIP, EFFECT_0)) + amount += cp * idol->GetAmount(); + + amount += int32(CalculatePct(caster->GetTotalAttackPowerValue(BASE_ATTACK), cp)); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_dru_rip_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_dru_rip_AuraScript(); + } +}; + // 62606 - Savage Defense class spell_dru_savage_defense : public SpellScriptLoader { @@ -658,6 +901,35 @@ class spell_dru_tiger_s_fury : public SpellScriptLoader } }; +// -61391 - Typhoon +class spell_dru_typhoon : public SpellScriptLoader +{ + public: + spell_dru_typhoon() : SpellScriptLoader("spell_dru_typhoon") { } + + class spell_dru_typhoon_SpellScript : public SpellScript + { + PrepareSpellScript(spell_dru_typhoon_SpellScript); + + void HandleKnockBack(SpellEffIndex effIndex) + { + // Glyph of Typhoon + if (GetCaster()->HasAura(SPELL_DRUID_GLYPH_OF_TYPHOON)) + PreventHitDefaultEffect(effIndex); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_dru_typhoon_SpellScript::HandleKnockBack, EFFECT_0, SPELL_EFFECT_KNOCK_BACK); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_dru_typhoon_SpellScript(); + } +}; + // 70691 - Item T10 Restoration 4P Bonus class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader { @@ -715,13 +987,20 @@ class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader void AddSC_druid_spell_scripts() { + new spell_dru_dash(); new spell_dru_enrage(); new spell_dru_glyph_of_starfire(); + new spell_dru_idol_lifebloom(); + new spell_dru_innervate(); new spell_dru_insect_swarm(); new spell_dru_lifebloom(); + new spell_dru_living_seed(); + new spell_dru_living_seed_proc(); new spell_dru_moonkin_form_passive(); + new spell_dru_owlkin_frenzy(); new spell_dru_predatory_strikes(); new spell_dru_primal_tenacity(); + new spell_dru_rip(); new spell_dru_savage_defense(); new spell_dru_savage_roar(); new spell_dru_starfall_aoe(); @@ -729,5 +1008,6 @@ void AddSC_druid_spell_scripts() new spell_dru_survival_instincts(); new spell_dru_swift_flight_passive(); new spell_dru_tiger_s_fury(); + new spell_dru_typhoon(); new spell_dru_t10_restoration_4p_bonus(); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index b5f4312bae5..05fad623114 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -72,6 +72,97 @@ class spell_gen_absorb0_hitlimit1 : public SpellScriptLoader } }; +// 28764 - Adaptive Warding (Frostfire Regalia Set) +enum AdaptiveWarding +{ + SPELL_GEN_ADAPTIVE_WARDING_FIRE = 28765, + SPELL_GEN_ADAPTIVE_WARDING_NATURE = 28768, + SPELL_GEN_ADAPTIVE_WARDING_FROST = 28766, + SPELL_GEN_ADAPTIVE_WARDING_SHADOW = 28769, + SPELL_GEN_ADAPTIVE_WARDING_ARCANE = 28770 +}; + +class spell_gen_adaptive_warding : public SpellScriptLoader +{ + public: + spell_gen_adaptive_warding() : SpellScriptLoader("spell_gen_adaptive_warding") { } + + class spell_gen_adaptive_warding_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_adaptive_warding_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_FIRE) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_NATURE) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_FROST) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_SHADOW) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_ADAPTIVE_WARDING_ARCANE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetDamageInfo()->GetSpellInfo()) // eventInfo.GetSpellInfo() + return false; + + // find Mage Armor + if (!GetTarget()->GetAuraEffect(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000, 0x0, 0x0)) + return false; + + switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) + { + case SPELL_SCHOOL_NORMAL: + case SPELL_SCHOOL_HOLY: + return false; + default: + break; + } + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId = 0; + switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) + { + case SPELL_SCHOOL_FIRE: + spellId = SPELL_GEN_ADAPTIVE_WARDING_FIRE; + break; + case SPELL_SCHOOL_NATURE: + spellId = SPELL_GEN_ADAPTIVE_WARDING_NATURE; + break; + case SPELL_SCHOOL_FROST: + spellId = SPELL_GEN_ADAPTIVE_WARDING_FROST; + break; + case SPELL_SCHOOL_SHADOW: + spellId = SPELL_GEN_ADAPTIVE_WARDING_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + spellId = SPELL_GEN_ADAPTIVE_WARDING_ARCANE; + break; + default: + return; + } + GetTarget()->CastSpell(GetTarget(), spellId, true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_gen_adaptive_warding_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_adaptive_warding_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_gen_adaptive_warding_AuraScript(); + } +}; + // 41337 Aura of Anger class spell_gen_aura_of_anger : public SpellScriptLoader { @@ -226,6 +317,245 @@ class spell_gen_cannibalize : public SpellScriptLoader } }; +// 63845 - Create Lance +enum CreateLanceSpells +{ + SPELL_CREATE_LANCE_ALLIANCE = 63914, + SPELL_CREATE_LANCE_HORDE = 63919 +}; + +class spell_gen_create_lance : public SpellScriptLoader +{ + public: + spell_gen_create_lance() : SpellScriptLoader("spell_gen_create_lance") { } + + class spell_gen_create_lance_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gen_create_lance_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_CREATE_LANCE_ALLIANCE) || !sSpellMgr->GetSpellInfo(SPELL_CREATE_LANCE_HORDE)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + if (Player* target = GetHitPlayer()) + { + if (target->GetTeam() == ALLIANCE) + GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_ALLIANCE, true); + else + GetCaster()->CastSpell(target, SPELL_CREATE_LANCE_HORDE, true); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_gen_create_lance_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_gen_create_lance_SpellScript(); + } +}; + +// 28702 - Netherbloom +enum Netherbloom +{ + SPELL_NETHERBLOOM_POLLEN_1 = 28703 +}; + +class spell_gen_netherbloom : public SpellScriptLoader +{ + public: + spell_gen_netherbloom() : SpellScriptLoader("spell_gen_netherbloom") { } + + class spell_gen_netherbloom_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gen_netherbloom_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + for (uint8 i = 0; i < 5; ++i) + if (!sSpellMgr->GetSpellInfo(SPELL_NETHERBLOOM_POLLEN_1 + i)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + if (Unit* target = GetHitUnit()) + { + // 25% chance of casting a random buff + if (roll_chance_i(75)) + return; + + // triggered spells are 28703 to 28707 + // Note: some sources say, that there was the possibility of + // receiving a debuff. However, this seems to be removed by a patch. + + // don't overwrite an existing aura + for (uint8 i = 0; i < 5; ++i) + if (target->HasAura(SPELL_NETHERBLOOM_POLLEN_1 + i)) + return; + + target->CastSpell(target, SPELL_NETHERBLOOM_POLLEN_1 + urand(0, 4), true); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_gen_netherbloom_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_gen_netherbloom_SpellScript(); + } +}; + +// 28720 - Nightmare Vine +enum NightmareVine +{ + SPELL_NIGHTMARE_POLLEN = 28721 +}; + +class spell_gen_nightmare_vine : public SpellScriptLoader +{ + public: + spell_gen_nightmare_vine() : SpellScriptLoader("spell_gen_nightmare_vine") { } + + class spell_gen_nightmare_vine_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gen_nightmare_vine_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_NIGHTMARE_POLLEN)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + if (Unit* target = GetHitUnit()) + { + // 25% chance of casting Nightmare Pollen + if (roll_chance_i(25)) + target->CastSpell(target, SPELL_NIGHTMARE_POLLEN, true); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_gen_nightmare_vine_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_gen_nightmare_vine_SpellScript(); + } +}; + +// 27539 - Obsidian Armor +enum ObsidianArmor +{ + SPELL_GEN_OBSIDIAN_ARMOR_HOLY = 27536, + SPELL_GEN_OBSIDIAN_ARMOR_FIRE = 27533, + SPELL_GEN_OBSIDIAN_ARMOR_NATURE = 27538, + SPELL_GEN_OBSIDIAN_ARMOR_FROST = 27534, + SPELL_GEN_OBSIDIAN_ARMOR_SHADOW = 27535, + SPELL_GEN_OBSIDIAN_ARMOR_ARCANE = 27540 +}; + +class spell_gen_obsidian_armor : public SpellScriptLoader +{ + public: + spell_gen_obsidian_armor() : SpellScriptLoader("spell_gen_obsidian_armor") { } + + class spell_gen_obsidian_armor_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_obsidian_armor_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_HOLY) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_FIRE) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_NATURE) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_FROST) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_SHADOW) || + !sSpellMgr->GetSpellInfo(SPELL_GEN_OBSIDIAN_ARMOR_ARCANE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetDamageInfo()->GetSpellInfo()) // eventInfo.GetSpellInfo() + return false; + + if (GetFirstSchoolInMask(eventInfo.GetSchoolMask()) == SPELL_SCHOOL_NORMAL) + return false; + + return true; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 spellId = 0; + switch (GetFirstSchoolInMask(eventInfo.GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_HOLY; + break; + case SPELL_SCHOOL_FIRE: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_FIRE; + break; + case SPELL_SCHOOL_NATURE: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_NATURE; + break; + case SPELL_SCHOOL_FROST: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_FROST; + break; + case SPELL_SCHOOL_SHADOW: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + spellId = SPELL_GEN_OBSIDIAN_ARMOR_ARCANE; + break; + default: + return; + } + GetTarget()->CastSpell(GetTarget(), spellId, true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_gen_obsidian_armor_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_obsidian_armor_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_gen_obsidian_armor_AuraScript(); + } +}; + // 45472 Parachute enum ParachuteSpells { @@ -3191,10 +3521,15 @@ class spell_gen_replenishment : public SpellScriptLoader void AddSC_generic_spell_scripts() { new spell_gen_absorb0_hitlimit1(); + new spell_gen_adaptive_warding(); new spell_gen_aura_of_anger(); new spell_gen_av_drekthar_presence(); new spell_gen_burn_brutallus(); new spell_gen_cannibalize(); + new spell_gen_create_lance(); + new spell_gen_netherbloom(); + new spell_gen_nightmare_vine(); + new spell_gen_obsidian_armor(); new spell_gen_parachute(); new spell_gen_pet_summoned(); new spell_gen_remove_flight_auras(); diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index d883b4d7da7..dbfc2b44501 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -303,27 +303,10 @@ class spell_winter_veil_mistletoe : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { - Unit* caster = GetCaster(); - if (Player* target = GetHitPlayer()) { - uint32 spellId = 0; - switch (urand(0, 2)) - { - case 0: - spellId = SPELL_CREATE_MISTLETOE; - break; - case 1: - spellId = SPELL_CREATE_HOLLY; - break; - case 2: - spellId = SPELL_CREATE_SNOWFLAKES; - break; - default: - return; - } - - caster->CastSpell(target, spellId, true); + uint32 spellId = RAND(SPELL_CREATE_HOLLY, SPELL_CREATE_MISTLETOE, SPELL_CREATE_SNOWFLAKES); + GetCaster()->CastSpell(target, spellId, true); } } @@ -339,6 +322,71 @@ class spell_winter_veil_mistletoe : public SpellScriptLoader } }; +// 26275 - PX-238 Winter Wondervolt TRAP +enum PX238WinterWondervolt +{ + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1 = 26157, + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2 = 26272, + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3 = 26273, + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4 = 26274 +}; + +class spell_winter_veil_px_238_winter_wondervolt : public SpellScriptLoader +{ + public: + spell_winter_veil_px_238_winter_wondervolt() : SpellScriptLoader("spell_winter_veil_px_238_winter_wondervolt") { } + + class spell_winter_veil_px_238_winter_wondervolt_SpellScript : public SpellScript + { + PrepareSpellScript(spell_winter_veil_px_238_winter_wondervolt_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1) || + !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2) || + !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3) || + !sSpellMgr->GetSpellInfo(SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + uint32 const spells[4] = + { + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_1, + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_2, + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_3, + SPELL_PX_238_WINTER_WONDERVOLT_TRANSFORM_4 + }; + + if (Unit* target = GetHitUnit()) + { + for (uint8 i = 0; i < 4; ++i) + if (target->HasAura(spells[i])) + return; + + GetCaster()->CastSpell(target, spells[urand(0, 3)], true); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_winter_veil_px_238_winter_wondervolt_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + + private: + + }; + + SpellScript* GetSpellScript() const + { + return new spell_winter_veil_px_238_winter_wondervolt_SpellScript(); + } +}; + void AddSC_holiday_spell_scripts() { // Love is in the Air @@ -349,4 +397,5 @@ void AddSC_holiday_spell_scripts() new spell_hallow_end_tricky_treat(); // Winter Veil new spell_winter_veil_mistletoe(); + new spell_winter_veil_px_238_winter_wondervolt(); } diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index a9b21807899..877248cf1a8 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -33,12 +33,15 @@ enum HunterSpells { SPELL_HUNTER_ASPECT_OF_THE_BEAST_PET = 61669, + SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE = 34075, SPELL_HUNTER_BESTIAL_WRATH = 19574, SPELL_HUNTER_CHIMERA_SHOT_SERPENT = 53353, SPELL_HUNTER_CHIMERA_SHOT_VIPER = 53358, SPELL_HUNTER_CHIMERA_SHOT_SCORPID = 53359, + SPELL_HUNTER_GLYPHE_OF_ASPECT_OF_THE_VIPER = 56851, SPELL_HUNTER_INVIGORATION_TRIGGERED = 53398, SPELL_HUNTER_MASTERS_CALL_TRIGGERED = 62305, + SPELL_HUNTER_MISDIRECTION_PROC = 35079, SPELL_HUNTER_PET_LAST_STAND_TRIGGERED = 53479, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX = 55709, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED = 54114, @@ -99,6 +102,50 @@ class spell_hun_aspect_of_the_beast : public SpellScriptLoader } }; +// 34074 - Aspect of the Viper +class spell_hun_ascpect_of_the_viper : public SpellScriptLoader +{ + public: + spell_hun_ascpect_of_the_viper() : SpellScriptLoader("spell_hun_ascpect_of_the_viper") { } + + class spell_hun_ascpect_of_the_viper_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_ascpect_of_the_viper_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_GLYPHE_OF_ASPECT_OF_THE_VIPER)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + uint32 maxMana = GetTarget()->GetMaxPower(POWER_MANA); + int32 mana = CalculatePct(maxMana, GetTarget()->GetAttackTime(RANGED_ATTACK) / 1000.0f); + + if (AuraEffect const* glyphe = GetTarget()->GetAuraEffect(SPELL_HUNTER_GLYPHE_OF_ASPECT_OF_THE_VIPER, EFFECT_0)) + AddPct(mana, glyphe->GetAmount()); + + GetTarget()->CastCustomSpell(SPELL_HUNTER_ASPECT_OF_THE_VIPER_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_hun_ascpect_of_the_viper_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_OBS_MOD_POWER); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_hun_ascpect_of_the_viper_AuraScript(); + } +}; + // 53209 - Chimera Shot class spell_hun_chimera_shot : public SpellScriptLoader { @@ -358,16 +405,35 @@ class spell_hun_misdirection : public SpellScriptLoader { PrepareAuraScript(spell_hun_misdirection_AuraScript); + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_MISDIRECTION_PROC)) + return false; + return true; + } + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* caster = GetCaster()) - if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT) - caster->SetReducedThreatPercent(0, 0); + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT) + GetTarget()->ResetRedirectThreat(); + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return GetTarget()->GetRedirectThreatTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_MISDIRECTION_PROC, true, NULL, aurEff); } void Register() { AfterEffectRemove += AuraEffectRemoveFn(spell_hun_misdirection_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + DoCheckProc += AuraCheckProcFn(spell_hun_misdirection_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_misdirection_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); } }; @@ -377,7 +443,7 @@ class spell_hun_misdirection : public SpellScriptLoader } }; -// 35079 - Misdirection proc +// 35079 - Misdirection (Proc) class spell_hun_misdirection_proc : public SpellScriptLoader { public: @@ -389,8 +455,7 @@ class spell_hun_misdirection_proc : public SpellScriptLoader void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (GetCaster()) - GetCaster()->SetReducedThreatPercent(0, 0); + GetTarget()->ResetRedirectThreat(); } void Register() @@ -740,6 +805,7 @@ class spell_hun_target_only_pet_and_owner : public SpellScriptLoader void AddSC_hunter_spell_scripts() { new spell_hun_aspect_of_the_beast(); + new spell_hun_ascpect_of_the_viper(); new spell_hun_chimera_shot(); new spell_hun_disengage(); new spell_hun_invigoration(); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index b4e06cb6b48..b8e17f4ecca 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -73,6 +73,150 @@ class spell_item_trigger_spell : public SpellScriptLoader } }; +// 26400 - Arcane Shroud +class spell_item_arcane_shroud : public SpellScriptLoader +{ + public: + spell_item_arcane_shroud() : SpellScriptLoader("spell_item_arcane_shroud") { } + + class spell_item_arcane_shroud_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_arcane_shroud_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + int32 diff = GetUnitOwner()->getLevel() - 60; + if (diff > 0) + amount += 2 * diff; + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_arcane_shroud_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_arcane_shroud_AuraScript(); + } +}; + +// 64411 - Blessing of Ancient Kings (Val'anyr, Hammer of Ancient Kings) +enum BlessingOfAncientKings +{ + SPELL_PROTECTION_OF_ANCIENT_KINGS = 64413 +}; + +class spell_item_blessing_of_ancient_kings : public SpellScriptLoader +{ + public: + spell_item_blessing_of_ancient_kings() : SpellScriptLoader("spell_item_blessing_of_ancient_kings") { } + + class spell_item_blessing_of_ancient_kings_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_blessing_of_ancient_kings_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PROTECTION_OF_ANCIENT_KINGS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 absorb = int32(CalculatePct(eventInfo.GetHealInfo()->GetHeal(), 15.0f)); + if (AuraEffect* protEff = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PROTECTION_OF_ANCIENT_KINGS, 0, eventInfo.GetActor()->GetGUID())) + { + // The shield can grow to a maximum size of 20,000 damage absorbtion + protEff->SetAmount(std::min<int32>(protEff->GetAmount() + absorb, 20000)); + + // Refresh and return to prevent replacing the aura + aurEff->GetBase()->RefreshDuration(); + } + else + GetTarget()->CastCustomSpell(SPELL_PROTECTION_OF_ANCIENT_KINGS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_item_blessing_of_ancient_kings_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_blessing_of_ancient_kings_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_blessing_of_ancient_kings_AuraScript(); + } +}; + +// 8342 - Defibrillate (Goblin Jumper Cables) have 33% chance on success +// 22999 - Defibrillate (Goblin Jumper Cables XL) have 50% chance on success +// 54732 - Defibrillate (Gnomish Army Knife) have 67% chance on success +enum Defibrillate +{ + SPELL_GOBLIN_JUMPER_CABLES_FAIL = 8338, + SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL = 23055 +}; + +class spell_item_defibrillate : public SpellScriptLoader +{ + public: + spell_item_defibrillate(char const* name, uint8 chance, uint32 failSpell = 0) : SpellScriptLoader(name), _chance(chance), _failSpell(failSpell) { } + + class spell_item_defibrillate_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_defibrillate_SpellScript); + + public: + spell_item_defibrillate_SpellScript(uint8 chance, uint32 failSpell) : SpellScript(), _chance(chance), _failSpell(failSpell) { } + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (_failSpell && !sSpellMgr->GetSpellInfo(_failSpell)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + if (roll_chance_i(_chance)) + { + PreventHitDefaultEffect(effIndex); + if (_failSpell) + GetCaster()->CastSpell(GetCaster(), _failSpell, true, GetCastItem()); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_item_defibrillate_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_RESURRECT); + } + + private: + uint8 _chance; + uint32 _failSpell; + }; + + SpellScript* GetSpellScript() const + { + return new spell_item_defibrillate_SpellScript(_chance, _failSpell); + } + + private: + uint8 _chance; + uint32 _failSpell; +}; + // http://www.wowhead.com/item=6522 Deviate Fish // 8063 Deviate Fish enum DeviateFishSpells @@ -357,6 +501,47 @@ class spell_item_mingos_fortune_generator : public SpellScriptLoader } }; +// 71875, 71877 - Item - Black Bruise: Necrotic Touch Proc +enum NecroticTouch +{ + SPELL_ITEM_NECROTIC_TOUCH_PROC = 71879 +}; + +class spell_item_necrotic_touch : public SpellScriptLoader +{ + public: + spell_item_necrotic_touch() : SpellScriptLoader("spell_item_necrotic_touch") { } + + class spell_item_necrotic_touch_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_necrotic_touch_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_ITEM_NECROTIC_TOUCH_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 bp = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_ITEM_NECROTIC_TOUCH_PROC, SPELLVALUE_BASE_POINT0, bp, GetTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_item_necrotic_touch_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_necrotic_touch_AuraScript(); + } +}; + // http://www.wowhead.com/item=10720 Gnomish Net-o-Matic Projector // 13120 Net-o-Matic enum NetOMaticSpells @@ -464,6 +649,35 @@ class spell_item_noggenfogger_elixir : public SpellScriptLoader } }; +// 17512 - Piccolo of the Flaming Fire +class spell_item_piccolo_of_the_flaming_fire : public SpellScriptLoader +{ + public: + spell_item_piccolo_of_the_flaming_fire() : SpellScriptLoader("spell_item_piccolo_of_the_flaming_fire") { } + + class spell_item_piccolo_of_the_flaming_fire_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_piccolo_of_the_flaming_fire_SpellScript); + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (Player* target = GetHitPlayer()) + target->HandleEmoteCommand(EMOTE_STATE_DANCE); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_item_piccolo_of_the_flaming_fire_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_item_piccolo_of_the_flaming_fire_SpellScript(); + } +}; + // http://www.wowhead.com/item=6657 Savory Deviate Delight // 8213 Savory Deviate Delight enum SavoryDeviateDelight @@ -522,6 +736,262 @@ class spell_item_savory_deviate_delight : public SpellScriptLoader } }; +// 48129 - Scroll of Recall +// 60320 - Scroll of Recall II +// 60321 - Scroll of Recall III +enum ScrollOfRecall +{ + SPELL_SCROLL_OF_RECALL_I = 48129, + SPELL_SCROLL_OF_RECALL_II = 60320, + SPELL_SCROLL_OF_RECALL_III = 60321, + SPELL_LOST = 60444, + SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1 = 60323, + SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1 = 60328, +}; + +class spell_item_scroll_of_recall : public SpellScriptLoader +{ + public: + spell_item_scroll_of_recall() : SpellScriptLoader("spell_item_scroll_of_recall") { } + + class spell_item_scroll_of_recall_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_scroll_of_recall_SpellScript); + + bool Load() + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleScript(SpellEffIndex effIndex) + { + Unit* caster = GetCaster(); + uint8 maxSafeLevel = 0; + switch (GetSpellInfo()->Id) + { + case SPELL_SCROLL_OF_RECALL_I: // Scroll of Recall + maxSafeLevel = 40; + break; + case SPELL_SCROLL_OF_RECALL_II: // Scroll of Recall II + maxSafeLevel = 70; + break; + case SPELL_SCROLL_OF_RECALL_III: // Scroll of Recal III + maxSafeLevel = 80; + break; + default: + break; + } + + if (caster->getLevel() > maxSafeLevel) + { + caster->CastSpell(caster, SPELL_LOST, true); + + // ALLIANCE from 60323 to 60330 - HORDE from 60328 to 60335 + uint32 spellId = SPELL_SCROLL_OF_RECALL_FAIL_ALLIANCE_1; + if (GetCaster()->ToPlayer()->GetTeam() == HORDE) + spellId = SPELL_SCROLL_OF_RECALL_FAIL_HORDE_1; + + GetCaster()->CastSpell(GetCaster(), spellId + urand(0, 7), true); + + PreventHitDefaultEffect(effIndex); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_item_scroll_of_recall_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_TELEPORT_UNITS); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_item_scroll_of_recall_SpellScript(); + } +}; + +// 71169 - Shadow's Fate (Shadowmourne questline) +enum ShadowsFate +{ + SPELL_SOUL_FEAST = 71203, + QUEST_A_FEAST_OF_SOULS = 24547 +}; + +class spell_item_shadows_fate : public SpellScriptLoader +{ + public: + spell_item_shadows_fate() : SpellScriptLoader("spell_item_shadows_fate") { } + + class spell_item_shadows_fate_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_shadows_fate_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_FEAST)) + return false; + if (!sObjectMgr->GetQuestTemplate(QUEST_A_FEAST_OF_SOULS)) + return false; + return true; + } + + bool Load() + { + _procTarget = NULL; + return true; + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + _procTarget = GetCaster(); + return _procTarget && _procTarget->GetTypeId() == TYPEID_PLAYER && _procTarget->ToPlayer()->GetQuestStatus(QUEST_A_FEAST_OF_SOULS) == QUEST_STATUS_INCOMPLETE; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(_procTarget, SPELL_SOUL_FEAST, true); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_item_shadows_fate_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_shadows_fate_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + private: + Unit* _procTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_shadows_fate_AuraScript(); + } +}; + +enum Shadowmourne +{ + SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE = 71904, + SPELL_SHADOWMOURNE_SOUL_FRAGMENT = 71905, + SPELL_SHADOWMOURNE_VISUAL_LOW = 72521, + SPELL_SHADOWMOURNE_VISUAL_HIGH = 72523, + SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF = 73422, +}; + +// 71903 - Item - Shadowmourne Legendary +class spell_item_shadowmourne : public SpellScriptLoader +{ + public: + spell_item_shadowmourne() : SpellScriptLoader("spell_item_shadowmourne") { } + + class spell_item_shadowmourne_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_shadowmourne_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_SOUL_FRAGMENT)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (GetTarget()->HasAura(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) // cant collect shards while under effect of Chaos Bane buff + return false; + return eventInfo.GetProcTarget() && eventInfo.GetProcTarget()->isAlive(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_SHADOWMOURNE_SOUL_FRAGMENT, true, NULL, aurEff); + + // this can't be handled in AuraScript of SoulFragments because we need to know victim + if (Aura* soulFragments = GetTarget()->GetAura(SPELL_SHADOWMOURNE_SOUL_FRAGMENT)) + { + if (soulFragments->GetStackAmount() >= 10) + { + GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_SHADOWMOURNE_CHAOS_BANE_DAMAGE, true, NULL, aurEff); + soulFragments->Remove(); + } + } + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_item_shadowmourne_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_item_shadowmourne_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_shadowmourne_AuraScript(); + } +}; + +// 71905 - Soul Fragment +class spell_item_shadowmourne_soul_fragment : public SpellScriptLoader +{ + public: + spell_item_shadowmourne_soul_fragment() : SpellScriptLoader("spell_item_shadowmourne_soul_fragment") { } + + class spell_item_shadowmourne_soul_fragment_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_shadowmourne_soul_fragment_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_LOW) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_HIGH) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) + return false; + return true; + } + + void OnStackChange(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + switch (GetStackAmount()) + { + case 1: + target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_LOW, true); + break; + case 6: + target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); + target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_HIGH, true); + break; + case 10: + target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); + target->CastSpell(target, SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF, true); + break; + default: + break; + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); + target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); + } + + void Register() + { + AfterEffectApply += AuraEffectApplyFn(spell_item_shadowmourne_soul_fragment_AuraScript::OnStackChange, EFFECT_0, SPELL_AURA_MOD_STAT, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAL | AURA_EFFECT_HANDLE_REAPPLY)); + AfterEffectRemove += AuraEffectRemoveFn(spell_item_shadowmourne_soul_fragment_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_shadowmourne_soul_fragment_AuraScript(); + } +}; + // http://www.wowhead.com/item=7734 Six Demon Bag // 14537 Six Demon Bag enum SixDemonBagSpells @@ -593,6 +1063,35 @@ class spell_item_six_demon_bag : public SpellScriptLoader } }; +// 28862 - The Eye of Diminution +class spell_item_the_eye_of_diminution : public SpellScriptLoader +{ + public: + spell_item_the_eye_of_diminution() : SpellScriptLoader("spell_item_the_eye_of_diminution") { } + + class spell_item_the_eye_of_diminution_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_the_eye_of_diminution_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + int32 diff = GetUnitOwner()->getLevel() - 60; + if (diff > 0) + amount += diff; + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_item_the_eye_of_diminution_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_THREAT); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_item_the_eye_of_diminution_AuraScript(); + } +}; + // http://www.wowhead.com/item=44012 Underbelly Elixir // 59640 Underbelly Elixir enum UnderbellyElixirSpells @@ -646,70 +1145,6 @@ class spell_item_underbelly_elixir : public SpellScriptLoader } }; -enum eShadowmourneVisuals -{ - SPELL_SHADOWMOURNE_VISUAL_LOW = 72521, - SPELL_SHADOWMOURNE_VISUAL_HIGH = 72523, - SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF = 73422, -}; - -class spell_item_shadowmourne : public SpellScriptLoader -{ -public: - spell_item_shadowmourne() : SpellScriptLoader("spell_item_shadowmourne") { } - - class spell_item_shadowmourne_AuraScript : public AuraScript - { - PrepareAuraScript(spell_item_shadowmourne_AuraScript); - - bool Validate(SpellInfo const* /*spellEntry*/) - { - if (!sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_LOW) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_VISUAL_HIGH) || !sSpellMgr->GetSpellInfo(SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF)) - return false; - return true; - } - - void OnStackChange(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - switch (GetStackAmount()) - { - case 1: - target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_LOW, true); - break; - case 6: - target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); - target->CastSpell(target, SPELL_SHADOWMOURNE_VISUAL_HIGH, true); - break; - case 10: - target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); - target->CastSpell(target, SPELL_SHADOWMOURNE_CHAOS_BANE_BUFF, true); - break; - default: - break; - } - } - - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_LOW); - target->RemoveAurasDueToSpell(SPELL_SHADOWMOURNE_VISUAL_HIGH); - } - - void Register() - { - AfterEffectApply += AuraEffectApplyFn(spell_item_shadowmourne_AuraScript::OnStackChange, EFFECT_0, SPELL_AURA_MOD_STAT, AuraEffectHandleModes(AURA_EFFECT_HANDLE_REAL | AURA_EFFECT_HANDLE_REAPPLY)); - AfterEffectRemove += AuraEffectRemoveFn(spell_item_shadowmourne_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const - { - return new spell_item_shadowmourne_AuraScript(); - } -}; - enum AirRifleSpells { SPELL_AIR_RIFLE_HOLD_VISUAL = 65582, @@ -2030,17 +2465,28 @@ void AddSC_item_spell_scripts() // 23075 Mithril Mechanical Dragonling new spell_item_trigger_spell("spell_item_mithril_mechanical_dragonling", SPELL_MITHRIL_MECHANICAL_DRAGONLING); + new spell_item_arcane_shroud(); + new spell_item_blessing_of_ancient_kings(); + new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL); + new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL); + new spell_item_defibrillate("spell_item_gnomish_army_knife", 33); new spell_item_deviate_fish(); new spell_item_flask_of_the_north(); new spell_item_gnomish_death_ray(); new spell_item_make_a_wish(); new spell_item_mingos_fortune_generator(); + new spell_item_necrotic_touch(); new spell_item_net_o_matic(); new spell_item_noggenfogger_elixir(); + new spell_item_piccolo_of_the_flaming_fire(); new spell_item_savory_deviate_delight(); + new spell_item_scroll_of_recall(); + new spell_item_shadows_fate(); + new spell_item_shadowmourne(); + new spell_item_shadowmourne_soul_fragment(); new spell_item_six_demon_bag(); + new spell_item_the_eye_of_diminution(); new spell_item_underbelly_elixir(); - new spell_item_shadowmourne(); new spell_item_red_rider_air_rifle(); new spell_item_create_heart_candy(); diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index 61f0579190f..ca835c61a3a 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -28,11 +28,15 @@ enum MageSpells { + SPELL_MAGE_BURNOUT = 29077, SPELL_MAGE_COLD_SNAP = 11958, - SPELL_MAGE_FROST_WARDING_R1 = 28332, + SPELL_MAGE_FOCUS_MAGIC_PROC = 54648, + SPELL_MAGE_FROST_WARDING_R1 = 11189, SPELL_MAGE_FROST_WARDING_TRIGGERED = 57776, SPELL_MAGE_INCANTERS_ABSORBTION_R1 = 44394, SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED = 44413, + SPELL_MAGE_IGNITE = 12654, + SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE = 29077, SPELL_MAGE_SQUIRREL_FORM = 32813, SPELL_MAGE_GIRAFFE_FORM = 32816, SPELL_MAGE_SERPENT_FORM = 32817, @@ -45,6 +49,31 @@ enum MageSpells SPELL_MAGE_GLYPH_OF_BLAST_WAVE = 62126, }; +// Incanter's Absorbtion +class spell_mage_incanters_absorbtion_base_AuraScript : public AuraScript +{ + public: + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_R1)) + return false; + return true; + } + + void Trigger(AuraEffect* aurEff, DamageInfo& /*dmgInfo*/, uint32& absorbAmount) + { + Unit* target = GetTarget(); + + if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_INCANTERS_ABSORBTION_R1, EFFECT_0)) + { + int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount()); + target->CastCustomSpell(target, SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff); + } + } +}; + // -11113 - Blast Wave class spell_mage_blast_wave : public SpellScriptLoader { @@ -80,6 +109,51 @@ class spell_mage_blast_wave : public SpellScriptLoader } }; +// -44449 - Burnout +class spell_mage_burnout : public SpellScriptLoader +{ + public: + spell_mage_burnout() : SpellScriptLoader("spell_mage_burnout") { } + + class spell_mage_burnout_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_burnout_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_BURNOUT)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetDamageInfo()->GetSpellInfo(); // eventInfo.GetSpellInfo() + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 mana = int32(eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask())); + mana = CalculatePct(mana, aurEff->GetAmount()); + + GetTarget()->CastCustomSpell(SPELL_MAGE_BURNOUT, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_mage_burnout_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_mage_burnout_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_mage_burnout_AuraScript(); + } +}; + // 11958 - Cold Snap class spell_mage_cold_snap : public SpellScriptLoader { @@ -127,29 +201,47 @@ class spell_mage_cold_snap : public SpellScriptLoader } }; -// -543, -6143 - Frost Warding -class spell_mage_frost_warding_trigger : public SpellScriptLoader +// -543 - Fire Ward +// -6143 - Frost Ward +class spell_mage_fire_frost_ward : public SpellScriptLoader { public: - spell_mage_frost_warding_trigger() : SpellScriptLoader("spell_mage_frost_warding_trigger") { } + spell_mage_fire_frost_ward() : SpellScriptLoader("spell_mage_fire_frost_ward") { } - class spell_mage_frost_warding_trigger_AuraScript : public AuraScript + class spell_mage_fire_frost_ward_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript { - PrepareAuraScript(spell_mage_frost_warding_trigger_AuraScript); + PrepareAuraScript(spell_mage_fire_frost_ward_AuraScript); bool Validate(SpellInfo const* /*spellInfo*/) { - if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_TRIGGERED) || !sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_R1)) + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_TRIGGERED)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FROST_WARDING_R1)) return false; return true; } - void Absorb(AuraEffect* aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount) + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated) + { + canBeRecalculated = false; + if (Unit* caster = GetCaster()) + { + // +80.68% from sp bonus + float bonus = 0.8068f; + + bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()); + bonus *= caster->CalculateLevelPenalty(GetSpellInfo()); + + amount += int32(bonus); + } + } + + void Absorb(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount) { Unit* target = GetTarget(); if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_FROST_WARDING_R1, EFFECT_0)) { - int32 chance = talentAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); + int32 chance = talentAurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // SPELL_EFFECT_DUMMY with NO_TARGET if (roll_chance_i(chance)) { @@ -164,79 +256,156 @@ class spell_mage_frost_warding_trigger : public SpellScriptLoader void Register() { - OnEffectAbsorb += AuraEffectAbsorbFn(spell_mage_frost_warding_trigger_AuraScript::Absorb, EFFECT_0); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_fire_frost_ward_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); + OnEffectAbsorb += AuraEffectAbsorbFn(spell_mage_fire_frost_ward_AuraScript::Absorb, EFFECT_0); + AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_fire_frost_ward_AuraScript::Trigger, EFFECT_0); } }; AuraScript* GetAuraScript() const { - return new spell_mage_frost_warding_trigger_AuraScript(); + return new spell_mage_fire_frost_ward_AuraScript(); } }; -class spell_mage_incanters_absorbtion_base_AuraScript : public AuraScript +// 54646 - Focus Magic +class spell_mage_focus_magic : public SpellScriptLoader { public: + spell_mage_focus_magic() : SpellScriptLoader("spell_mage_focus_magic") { } - bool Validate(SpellInfo const* /*spellInfo*/) + class spell_mage_focus_magic_AuraScript : public AuraScript { - return sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED) - && sSpellMgr->GetSpellInfo(SPELL_MAGE_INCANTERS_ABSORBTION_R1); - } + PrepareAuraScript(spell_mage_focus_magic_AuraScript); - void Trigger(AuraEffect* aurEff, DamageInfo & /*dmgInfo*/, uint32 & absorbAmount) - { - Unit* target = GetTarget(); + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_FOCUS_MAGIC_PROC)) + return false; + return true; + } - if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_MAGE_INCANTERS_ABSORBTION_R1, EFFECT_0)) + bool Load() { - int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount()); - target->CastCustomSpell(target, SPELL_MAGE_INCANTERS_ABSORBTION_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff); + _procTarget = NULL; + return true; + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + _procTarget = GetCaster(); + return _procTarget && _procTarget->isAlive(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(_procTarget, SPELL_MAGE_FOCUS_MAGIC_PROC, true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_mage_focus_magic_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_mage_focus_magic_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_MOD_SPELL_CRIT_CHANCE); } + + private: + Unit* _procTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_mage_focus_magic_AuraScript(); } }; -// -543, -6143, -11426 - Incanter's Absorption -class spell_mage_incanters_absorbtion_absorb : public SpellScriptLoader +// -11426 - Ice Barrier +class spell_mage_ice_barrier : public SpellScriptLoader { public: - spell_mage_incanters_absorbtion_absorb() : SpellScriptLoader("spell_mage_incanters_absorbtion_absorb") { } + spell_mage_ice_barrier() : SpellScriptLoader("spell_mage_ice_barrier") { } - class spell_mage_incanters_absorbtion_absorb_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript + class spell_mage_ice_barrier_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript { - PrepareAuraScript(spell_mage_incanters_absorbtion_absorb_AuraScript); + PrepareAuraScript(spell_mage_ice_barrier_AuraScript); + + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) + { + canBeRecalculated = false; + if (Unit* caster = GetCaster()) + { + // +80.68% from sp bonus + float bonus = 0.8068f; + + bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()); + + // Glyph of Ice Barrier: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :) + // Glyph of Ice Barrier is only applied at the spell damage bonus because it was already applied to the base value in CalculateSpellDamage + bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus); + + bonus *= caster->CalculateLevelPenalty(GetSpellInfo()); + + amount += int32(bonus); + } + } void Register() { - AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_incanters_absorbtion_absorb_AuraScript::Trigger, EFFECT_0); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_ice_barrier_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); + AfterEffectAbsorb += AuraEffectAbsorbFn(spell_mage_ice_barrier_AuraScript::Trigger, EFFECT_0); } }; AuraScript* GetAuraScript() const { - return new spell_mage_incanters_absorbtion_absorb_AuraScript(); + return new spell_mage_ice_barrier_AuraScript(); } }; -// -1463 - Incanter's Absorption -class spell_mage_incanters_absorbtion_manashield : public SpellScriptLoader +// -11119 - Ignite +class spell_mage_ignite : public SpellScriptLoader { public: - spell_mage_incanters_absorbtion_manashield() : SpellScriptLoader("spell_mage_incanters_absorbtion_manashield") { } + spell_mage_ignite() : SpellScriptLoader("spell_mage_ignite") { } - class spell_mage_incanters_absorbtion_manashield_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript + class spell_mage_ignite_AuraScript : public AuraScript { - PrepareAuraScript(spell_mage_incanters_absorbtion_manashield_AuraScript); + PrepareAuraScript(spell_mage_ignite_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_IGNITE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + SpellInfo const* igniteDot = sSpellMgr->GetSpellInfo(SPELL_MAGE_IGNITE); + int32 pct = 8 * GetSpellInfo()->GetRank(); + + int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), pct) / igniteDot->GetMaxTicks()); + amount += eventInfo.GetProcTarget()->GetRemainingPeriodicAmount(eventInfo.GetActor()->GetGUID(), SPELL_MAGE_IGNITE, SPELL_AURA_PERIODIC_DAMAGE); + GetTarget()->CastCustomSpell(SPELL_MAGE_IGNITE, SPELLVALUE_BASE_POINT0, amount, eventInfo.GetProcTarget(), true, NULL, aurEff); + } void Register() { - AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_incanters_absorbtion_manashield_AuraScript::Trigger, EFFECT_0); + DoCheckProc += AuraCheckProcFn(spell_mage_ignite_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_mage_ignite_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; AuraScript* GetAuraScript() const { - return new spell_mage_incanters_absorbtion_manashield_AuraScript(); + return new spell_mage_ignite_AuraScript(); } }; @@ -279,6 +448,90 @@ class spell_mage_living_bomb : public SpellScriptLoader } }; +// -1463 - Mana Shield +class spell_mage_mana_shield : public SpellScriptLoader +{ + public: + spell_mage_mana_shield() : SpellScriptLoader("spell_mage_mana_shield") { } + + class spell_mage_mana_shield_AuraScript : public spell_mage_incanters_absorbtion_base_AuraScript + { + PrepareAuraScript(spell_mage_mana_shield_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated) + { + canBeRecalculated = false; + if (Unit* caster = GetCaster()) + { + // +80.53% from sp bonus + float bonus = 0.8053f; + + bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()); + bonus *= caster->CalculateLevelPenalty(GetSpellInfo()); + + amount += int32(bonus); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_mage_mana_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MANA_SHIELD); + AfterEffectManaShield += AuraEffectManaShieldFn(spell_mage_mana_shield_AuraScript::Trigger, EFFECT_0); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_mage_mana_shield_AuraScript(); + } +}; + +// -29074 - Master of Elements +class spell_mage_master_of_elements : public SpellScriptLoader +{ + public: + spell_mage_master_of_elements() : SpellScriptLoader("spell_mage_master_of_elements") { } + + class spell_mage_master_of_elements_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_master_of_elements_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetDamageInfo()->GetSpellInfo(); // eventInfo.GetSpellInfo() + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 mana = int32(eventInfo.GetDamageInfo()->GetSpellInfo()->CalcPowerCost(GetTarget(), eventInfo.GetDamageInfo()->GetSchoolMask())); + mana = CalculatePct(mana, aurEff->GetAmount()); + + if (mana > 0) + GetTarget()->CastCustomSpell(SPELL_MAGE_MASTER_OF_ELEMENTS_ENERGIZE, SPELLVALUE_BASE_POINT0, mana, GetTarget(), true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_mage_master_of_elements_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_mage_master_of_elements_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_mage_master_of_elements_AuraScript(); + } +}; + enum SilvermoonPolymorph { NPC_AUROSALIA = 18744, @@ -377,11 +630,15 @@ class spell_mage_summon_water_elemental : public SpellScriptLoader void AddSC_mage_spell_scripts() { new spell_mage_blast_wave(); + new spell_mage_burnout(); new spell_mage_cold_snap(); - new spell_mage_frost_warding_trigger(); - new spell_mage_incanters_absorbtion_absorb(); - new spell_mage_incanters_absorbtion_manashield(); + new spell_mage_fire_frost_ward(); + new spell_mage_focus_magic(); + new spell_mage_ice_barrier(); + new spell_mage_ignite(); new spell_mage_living_bomb(); + new spell_mage_mana_shield(); + new spell_mage_master_of_elements(); new spell_mage_polymorph_cast_visual(); new spell_mage_summon_water_elemental(); } diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index d3fc86302e1..6be2453affb 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -31,6 +31,7 @@ enum PaladinSpells { SPELL_PALADIN_DIVINE_PLEA = 54428, SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF = 67480, + SPELL_PALADIN_BLESSING_OF_SANCTUARY_ENERGIZE = 57319, SPELL_PALADIN_HOLY_SHOCK_R1 = 20473, SPELL_PALADIN_HOLY_SHOCK_R1_DAMAGE = 25912, @@ -45,12 +46,23 @@ enum PaladinSpells SPELL_PALADIN_DIVINE_STORM_DUMMY = 54171, SPELL_PALADIN_DIVINE_STORM_HEAL = 54172, + SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE = 25997, + SPELL_PALADIN_FORBEARANCE = 25771, SPELL_PALADIN_AVENGING_WRATH_MARKER = 61987, SPELL_PALADIN_IMMUNE_SHIELD_MARKER = 61988, SPELL_PALADIN_HAND_OF_SACRIFICE = 6940, SPELL_PALADIN_DIVINE_SACRIFICE = 64205, + + SPELL_PALADIN_GLYPH_OF_SALVATION = 63225, + + SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT = 31790, + + SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS = 25742, + + SPELL_GENERIC_ARENA_DAMPENING = 74410, + SPELL_GENERIC_BATTLEGROUND_DAMPENING = 74411 }; // 31850 - Ardent Defender @@ -185,8 +197,8 @@ class spell_pal_blessing_of_faith : public SpellScriptLoader } }; -// 20911 Blessing of Sanctuary -// 25899 Greater Blessing of Sanctuary +// 20911 - Blessing of Sanctuary +// 25899 - Greater Blessing of Sanctuary class spell_pal_blessing_of_sanctuary : public SpellScriptLoader { public: @@ -200,6 +212,8 @@ class spell_pal_blessing_of_sanctuary : public SpellScriptLoader { if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF)) return false; + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_BLESSING_OF_SANCTUARY_ENERGIZE)) + return false; return true; } @@ -216,10 +230,23 @@ class spell_pal_blessing_of_sanctuary : public SpellScriptLoader target->RemoveAura(SPELL_PALADIN_BLESSING_OF_SANCTUARY_BUFF, GetCasterGUID()); } + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return GetTarget()->getPowerType() == POWER_MANA; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(GetTarget(), SPELL_PALADIN_BLESSING_OF_SANCTUARY_ENERGIZE, true, NULL, aurEff); + } + void Register() { AfterEffectApply += AuraEffectApplyFn(spell_pal_blessing_of_sanctuary_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); AfterEffectRemove += AuraEffectRemoveFn(spell_pal_blessing_of_sanctuary_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + DoCheckProc += AuraCheckProcFn(spell_pal_blessing_of_sanctuary_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pal_blessing_of_sanctuary_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); } }; @@ -410,6 +437,43 @@ class spell_pal_exorcism_and_holy_wrath_damage : public SpellScriptLoader } }; +// -9799 - Eye for an Eye +class spell_pal_eye_for_an_eye : public SpellScriptLoader +{ + public: + spell_pal_eye_for_an_eye() : SpellScriptLoader("spell_pal_eye_for_an_eye") { } + + class spell_pal_eye_for_an_eye_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_eye_for_an_eye_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE)) + return false; + return true; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + // return damage % to attacker but < 50% own total health + int32 damage = int32(std::min(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()), GetTarget()->GetMaxHealth() / 2)); + GetTarget()->CastCustomSpell(SPELL_PALADIN_EYE_FOR_AN_EYE_DAMAGE, SPELLVALUE_BASE_POINT0, damage, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_pal_eye_for_an_eye_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pal_eye_for_an_eye_AuraScript(); + } +}; + // 63521 - Guarded by The Light class spell_pal_guarded_by_the_light : public SpellScriptLoader { @@ -490,6 +554,39 @@ class spell_pal_hand_of_sacrifice : public SpellScriptLoader } }; +// 1038 - Hand of Salvation +class spell_pal_hand_of_salvation : public SpellScriptLoader +{ + public: + spell_pal_hand_of_salvation() : SpellScriptLoader("spell_pal_hand_of_salvation") { } + + class spell_pal_hand_of_salvation_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_hand_of_salvation_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* caster = GetCaster()) + { + // Glyph of Salvation + if (caster->GetGUID() == GetUnitOwner()->GetGUID()) + if (AuraEffect const* aurEff = caster->GetAuraEffect(SPELL_PALADIN_GLYPH_OF_SALVATION, EFFECT_0)) + amount -= aurEff->GetAmount(); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pal_hand_of_salvation_AuraScript::CalculateAmount, EFFECT_1, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pal_hand_of_salvation_AuraScript(); + } +}; + // -20473 - Holy Shock class spell_pal_holy_shock : public SpellScriptLoader { @@ -656,6 +753,13 @@ class spell_pal_righteous_defense : public SpellScriptLoader { PrepareSpellScript(spell_pal_righteous_defense_SpellScript); + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT)) + return false; + return true; + } + SpellCastResult CheckCast() { Unit* caster = GetCaster(); @@ -673,9 +777,27 @@ class spell_pal_righteous_defense : public SpellScriptLoader return SPELL_CAST_OK; } + void HandleTriggerSpellLaunch(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + } + + void HandleTriggerSpellHit(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + if (Unit* target = GetHitUnit()) + GetCaster()->CastSpell(target, SPELL_PALADIN_RIGHTEOUS_DEFENSE_TAUNT, true); + } + void Register() { OnCheckCast += SpellCheckCastFn(spell_pal_righteous_defense_SpellScript::CheckCast); + //! WORKAROUND + //! target select will be executed in hitphase of effect 0 + //! so we must handle trigger spell also in hit phase (default execution in launch phase) + //! see issue #3718 + OnEffectLaunchTarget += SpellEffectFn(spell_pal_righteous_defense_SpellScript::HandleTriggerSpellLaunch, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL); + OnEffectHitTarget += SpellEffectFn(spell_pal_righteous_defense_SpellScript::HandleTriggerSpellHit, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL); } }; @@ -685,6 +807,96 @@ class spell_pal_righteous_defense : public SpellScriptLoader } }; +// 58597 - Sacred Shield +class spell_pal_sacred_shield : public SpellScriptLoader +{ + public: + spell_pal_sacred_shield() : SpellScriptLoader("spell_pal_sacred_shield") { } + + class spell_pal_sacred_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_sacred_shield_AuraScript); + + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* caster = GetCaster()) + { + // +75.00% from sp bonus + float bonus = CalculatePct(caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()), 75.0f); + + // Divine Guardian is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage + bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus); + bonus *= caster->CalculateLevelPenalty(GetSpellInfo()); + + amount += int32(bonus); + + // Arena - Dampening + if (AuraEffect const* dampening = caster->GetAuraEffect(SPELL_GENERIC_ARENA_DAMPENING, EFFECT_0)) + AddPct(amount, dampening->GetAmount()); + // Battleground - Dampening + else if (AuraEffect const* dampening = caster->GetAuraEffect(SPELL_GENERIC_BATTLEGROUND_DAMPENING, EFFECT_0)) + AddPct(amount, dampening->GetAmount()); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pal_sacred_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pal_sacred_shield_AuraScript(); + } +}; + +// 20154, 21084 - Seal of Righteousness - melee proc dummy (addition ${$MWS*(0.022*$AP+0.044*$SPH)} damage) +class spell_pal_seal_of_righteousness : public SpellScriptLoader +{ + public: + spell_pal_seal_of_righteousness() : SpellScriptLoader("spell_pal_seal_of_righteousness") { } + + class spell_pal_seal_of_righteousness_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_seal_of_righteousness_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + float ap = GetTarget()->GetTotalAttackPowerValue(BASE_ATTACK); + int32 holy = GetTarget()->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_HOLY); + holy += eventInfo.GetProcTarget()->SpellBaseDamageBonusTaken(SPELL_SCHOOL_MASK_HOLY); + int32 bp = int32((ap * 0.022f + 0.044f * holy) * GetTarget()->GetAttackTime(BASE_ATTACK) / 1000); + GetTarget()->CastCustomSpell(SPELL_PALADIN_SEAL_OF_RIGHTEOUSNESS, SPELLVALUE_BASE_POINT0, bp, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_pal_seal_of_righteousness_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pal_seal_of_righteousness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pal_seal_of_righteousness_AuraScript(); + } +}; + void AddSC_paladin_spell_scripts() { new spell_pal_ardent_defender(); @@ -694,10 +906,14 @@ void AddSC_paladin_spell_scripts() new spell_pal_divine_storm(); new spell_pal_divine_storm_dummy(); new spell_pal_exorcism_and_holy_wrath_damage(); + new spell_pal_eye_for_an_eye(); new spell_pal_guarded_by_the_light(); new spell_pal_hand_of_sacrifice(); + new spell_pal_hand_of_salvation(); new spell_pal_holy_shock(); new spell_pal_judgement_of_command(); new spell_pal_lay_on_hands(); new spell_pal_righteous_defense(); + new spell_pal_sacred_shield(); + new spell_pal_seal_of_righteousness(); } diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 1c416a0d28a..e19110d0f62 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -29,22 +29,115 @@ enum PriestSpells { - SPELL_PRIEST_EMPOWERED_RENEW = 63544, - SPELL_PRIEST_GUARDIAN_SPIRIT_HEAL = 48153, - SPELL_PRIEST_PENANCE_R1 = 47540, - SPELL_PRIEST_PENANCE_R1_DAMAGE = 47758, - SPELL_PRIEST_PENANCE_R1_HEAL = 47757, - SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED = 33619, - SPELL_PRIEST_REFLECTIVE_SHIELD_R1 = 33201, - SPELL_PRIEST_SHADOW_WORD_DEATH = 32409, - SPELL_PRIEST_T9_HEALING_2P = 67201, - SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL = 64085, + SPELL_PRIEST_DIVINE_AEGIS = 47753, + SPELL_PRIEST_EMPOWERED_RENEW = 63544, + SPELL_PRIEST_GLYPHE_OF_LIGHTWELL = 55673, + SPELL_PRIEST_GLYPHE_OF_PRAYER_OF_HEALING_HEAL = 56161, + SPELL_PRIEST_GUARDIAN_SPIRIT_HEAL = 48153, + SPELL_PRIEST_MANA_LEECH_PROC = 34650, + SPELL_PRIEST_PENANCE_R1 = 47540, + SPELL_PRIEST_PENANCE_R1_DAMAGE = 47758, + SPELL_PRIEST_PENANCE_R1_HEAL = 47757, + SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED = 33619, + SPELL_PRIEST_REFLECTIVE_SHIELD_R1 = 33201, + SPELL_PRIEST_SHADOW_WORD_DEATH = 32409, + SPELL_PRIEST_T9_HEALING_2P = 67201, + SPELL_PRIEST_VAMPIRIC_TOUCH_DISPEL = 64085, }; enum PriestSpellIcons { - PRIEST_ICON_ID_EMPOWERED_RENEW_TALENT = 3021, - PRIEST_ICON_ID_PAIN_AND_SUFFERING = 2874, + PRIEST_ICON_ID_BORROWED_TIME = 2899, + PRIEST_ICON_ID_EMPOWERED_RENEW_TALENT = 3021, + PRIEST_ICON_ID_PAIN_AND_SUFFERING = 2874, +}; + +// -47509 - Divine Aegis +class spell_pri_divine_aegis : public SpellScriptLoader +{ + public: + spell_pri_divine_aegis() : SpellScriptLoader("spell_pri_divine_aegis") { } + + class spell_pri_divine_aegis_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_divine_aegis_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_DIVINE_AEGIS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetProcTarget(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 absorb = CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()); + + // Multiple effects stack, so let's try to find this aura. + if (AuraEffect const* aegis = eventInfo.GetProcTarget()->GetAuraEffect(SPELL_PRIEST_DIVINE_AEGIS, EFFECT_0)) + absorb += aegis->GetAmount(); + + absorb = std::min(absorb, eventInfo.GetProcTarget()->getLevel() * 125); + + GetTarget()->CastCustomSpell(SPELL_PRIEST_DIVINE_AEGIS, SPELLVALUE_BASE_POINT0, absorb, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_pri_divine_aegis_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_divine_aegis_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pri_divine_aegis_AuraScript(); + } +}; + +// 55680 - Glyph of Prayer of Healing +class spell_pri_glyph_of_prayer_of_healing : public SpellScriptLoader +{ + public: + spell_pri_glyph_of_prayer_of_healing() : SpellScriptLoader("spell_pri_glyph_of_prayer_of_healing") { } + + class spell_pri_glyph_of_prayer_of_healing_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_glyph_of_prayer_of_healing_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPHE_OF_PRAYER_OF_HEALING_HEAL)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(SPELL_PRIEST_GLYPHE_OF_PRAYER_OF_HEALING_HEAL); + int32 heal = int32(CalculatePct(int32(eventInfo.GetHealInfo()->GetHeal()), aurEff->GetAmount()) / triggeredSpellInfo->GetMaxTicks()); + GetTarget()->CastCustomSpell(SPELL_PRIEST_GLYPHE_OF_PRAYER_OF_HEALING_HEAL, SPELLVALUE_BASE_POINT0, heal, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_pri_glyph_of_prayer_of_healing_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pri_glyph_of_prayer_of_healing_AuraScript(); + } }; // -47788 - Guardian Spirit @@ -104,6 +197,38 @@ class spell_pri_guardian_spirit : public SpellScriptLoader } }; +// -7001 - Lightwell Renew +class spell_pri_lightwell_renew : public SpellScriptLoader +{ + public: + spell_pri_lightwell_renew() : SpellScriptLoader("spell_pri_lightwell_renew") { } + + class spell_pri_lightwell_renew_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_lightwell_renew_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& /*canBeRecalculated*/) + { + if (Unit* caster = GetCaster()) + { + // Bonus from Glyph of Lightwell + if (AuraEffect* modHealing = caster->GetAuraEffect(SPELL_PRIEST_GLYPHE_OF_LIGHTWELL, EFFECT_0)) + AddPct(amount, modHealing->GetAmount()); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_lightwell_renew_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_HEAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_pri_lightwell_renew_AuraScript(); + } +}; + // -8129 - Mana Burn class spell_pri_mana_burn : public SpellScriptLoader { @@ -132,6 +257,57 @@ class spell_pri_mana_burn : public SpellScriptLoader } }; +// 28305 - Mana Leech (Passive) (Priest Pet Aura) +class spell_pri_mana_leech : public SpellScriptLoader +{ + public: + spell_pri_mana_leech() : SpellScriptLoader("spell_pri_mana_leech") { } + + class spell_pri_mana_leech_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_mana_leech_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_MANA_LEECH_PROC)) + return false; + return true; + } + + bool Load() + { + _procTarget = NULL; + return true; + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + _procTarget = GetTarget()->GetOwner(); + return _procTarget; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(_procTarget, SPELL_PRIEST_MANA_LEECH_PROC, true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_pri_mana_leech_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_pri_mana_leech_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + private: + Unit* _procTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_pri_mana_leech_AuraScript(); + } +}; + // -49821 - Mind Sear class spell_pri_mind_sear : public SpellScriptLoader { @@ -260,81 +436,114 @@ class spell_pri_penance : public SpellScriptLoader } }; -// 33110 - Prayer of Mending Heal -class spell_pri_prayer_of_mending_heal : public SpellScriptLoader +// -17 - Power Word: Shield +class spell_pri_power_word_shield : public SpellScriptLoader { public: - spell_pri_prayer_of_mending_heal() : SpellScriptLoader("spell_pri_prayer_of_mending_heal") { } + spell_pri_power_word_shield() : SpellScriptLoader("spell_pri_power_word_shield") { } - class spell_pri_prayer_of_mending_heal_SpellScript : public SpellScript + class spell_pri_power_word_shield_AuraScript : public AuraScript { - PrepareSpellScript(spell_pri_prayer_of_mending_heal_SpellScript); + PrepareAuraScript(spell_pri_power_word_shield_AuraScript); - void HandleHeal(SpellEffIndex /*effIndex*/) + bool Validate(SpellInfo const* /*spellInfo*/) { - if (Unit* caster = GetOriginalCaster()) + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_R1)) + return false; + return true; + } + + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) + { + canBeRecalculated = false; + if (Unit* caster = GetCaster()) { - if (AuraEffect* aurEff = caster->GetAuraEffect(SPELL_PRIEST_T9_HEALING_2P, EFFECT_0)) + // +80.68% from sp bonus + float bonus = 0.8068f; + + // Borrowed Time + if (AuraEffect const* borrowedTime = caster->GetDummyAuraEffect(SPELLFAMILY_PRIEST, PRIEST_ICON_ID_BORROWED_TIME, EFFECT_1)) + bonus += CalculatePct(1.0f, borrowedTime->GetAmount()); + + bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()); + + // Improved PW: Shield: its weird having a SPELLMOD_ALL_EFFECTS here but its blizzards doing :) + // Improved PW: Shield is only applied at the spell healing bonus because it was already applied to the base value in CalculateSpellDamage + bonus = caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), bonus); + bonus *= caster->CalculateLevelPenalty(GetSpellInfo()); + + amount += int32(bonus); + + // Twin Disciplines + if (AuraEffect const* twinDisciplines = caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PRIEST, 0x400000, 0, 0, GetCasterGUID())) + AddPct(amount, twinDisciplines->GetAmount()); + + // Focused Power + amount *= caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALING_DONE_PERCENT); + } + } + + void ReflectDamage(AuraEffect* aurEff, DamageInfo& dmgInfo, uint32& absorbAmount) + { + Unit* target = GetTarget(); + if (dmgInfo.GetAttacker() == target) + return; + + if (Unit* caster = GetCaster()) + if (AuraEffect* talentAurEff = caster->GetAuraEffectOfRankedSpell(SPELL_PRIEST_REFLECTIVE_SHIELD_R1, EFFECT_0)) { - int32 heal = GetHitHeal(); - AddPct(heal, aurEff->GetAmount()); - SetHitHeal(heal); + int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount()); + target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff); } - } } void Register() { - OnEffectHitTarget += SpellEffectFn(spell_pri_prayer_of_mending_heal_SpellScript::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_pri_power_word_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); + AfterEffectAbsorb += AuraEffectAbsorbFn(spell_pri_power_word_shield_AuraScript::ReflectDamage, EFFECT_0); } }; - SpellScript* GetSpellScript() const + AuraScript* GetAuraScript() const { - return new spell_pri_prayer_of_mending_heal_SpellScript(); + return new spell_pri_power_word_shield_AuraScript(); } }; -// -17 - Reflective Shield -class spell_pri_reflective_shield_trigger : public SpellScriptLoader +// 33110 - Prayer of Mending Heal +class spell_pri_prayer_of_mending_heal : public SpellScriptLoader { public: - spell_pri_reflective_shield_trigger() : SpellScriptLoader("spell_pri_reflective_shield_trigger") { } + spell_pri_prayer_of_mending_heal() : SpellScriptLoader("spell_pri_prayer_of_mending_heal") { } - class spell_pri_reflective_shield_trigger_AuraScript : public AuraScript + class spell_pri_prayer_of_mending_heal_SpellScript : public SpellScript { - PrepareAuraScript(spell_pri_reflective_shield_trigger_AuraScript); - - bool Validate(SpellInfo const* /*spellInfo*/) - { - if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED) || !sSpellMgr->GetSpellInfo(SPELL_PRIEST_REFLECTIVE_SHIELD_R1)) - return false; - return true; - } + PrepareSpellScript(spell_pri_prayer_of_mending_heal_SpellScript); - void Trigger(AuraEffect* aurEff, DamageInfo & dmgInfo, uint32 & absorbAmount) + void HandleHeal(SpellEffIndex /*effIndex*/) { - Unit* target = GetTarget(); - if (dmgInfo.GetAttacker() == target) - return; - - if (GetCaster()) - if (AuraEffect* talentAurEff = target->GetAuraEffectOfRankedSpell(SPELL_PRIEST_REFLECTIVE_SHIELD_R1, EFFECT_0)) + if (Unit* caster = GetOriginalCaster()) + { + if (AuraEffect* aurEff = caster->GetAuraEffect(SPELL_PRIEST_T9_HEALING_2P, EFFECT_0)) { - int32 bp = CalculatePct(absorbAmount, talentAurEff->GetAmount()); - target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_PRIEST_REFLECTIVE_SHIELD_TRIGGERED, &bp, NULL, NULL, true, NULL, aurEff); + int32 heal = GetHitHeal(); + AddPct(heal, aurEff->GetAmount()); + SetHitHeal(heal); } + } } void Register() { - AfterEffectAbsorb += AuraEffectAbsorbFn(spell_pri_reflective_shield_trigger_AuraScript::Trigger, EFFECT_0); + OnEffectHitTarget += SpellEffectFn(spell_pri_prayer_of_mending_heal_SpellScript::HandleHeal, EFFECT_0, SPELL_EFFECT_HEAL); } }; - AuraScript* GetAuraScript() const + SpellScript* GetSpellScript() const { - return new spell_pri_reflective_shield_trigger_AuraScript(); + return new spell_pri_prayer_of_mending_heal_SpellScript(); } }; @@ -457,13 +666,17 @@ class spell_pri_vampiric_touch : public SpellScriptLoader void AddSC_priest_spell_scripts() { + new spell_pri_divine_aegis(); + new spell_pri_glyph_of_prayer_of_healing(); new spell_pri_guardian_spirit(); + new spell_pri_lightwell_renew(); new spell_pri_mana_burn(); + new spell_pri_mana_leech(); new spell_pri_mind_sear(); new spell_pri_pain_and_suffering_proc(); new spell_pri_penance(); + new spell_pri_power_word_shield(); new spell_pri_prayer_of_mending_heal(); - new spell_pri_reflective_shield_trigger(); new spell_pri_renew(); new spell_pri_shadow_word_death(); new spell_pri_vampiric_touch(); diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index 70b677f5aaa..f6391c80d63 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -28,10 +28,68 @@ enum RogueSpells { + SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK = 22482, SPELL_ROGUE_CHEAT_DEATH_COOLDOWN = 31231, SPELL_ROGUE_GLYPH_OF_PREPARATION = 56819, SPELL_ROGUE_PREY_ON_THE_WEAK = 58670, SPELL_ROGUE_SHIV_TRIGGERED = 5940, + SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST = 57933, + SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC = 59628, +}; + +// 13877, 33735, (check 51211, 65956) - Blade Flurry +class spell_rog_blade_flurry : public SpellScriptLoader +{ + public: + spell_rog_blade_flurry() : SpellScriptLoader("spell_rog_blade_flurry") { } + + class spell_rog_blade_flurry_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_blade_flurry_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK)) + return false; + return true; + } + + bool Load() + { + _procTarget = NULL; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + _procTarget = eventInfo.GetActor()->SelectNearbyTarget(eventInfo.GetProcTarget()); + return _procTarget; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (eventInfo.GetDamageInfo()) + { + int32 damage = eventInfo.GetDamageInfo()->GetDamage(); + GetTarget()->CastCustomSpell(SPELL_ROGUE_BLADE_FLURRY_EXTRA_ATTACK, SPELLVALUE_BASE_POINT0, damage, _procTarget, true, NULL, aurEff); + } + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_rog_blade_flurry_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_rog_blade_flurry_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_MOD_MELEE_HASTE); + } + + private: + Unit* _procTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_rog_blade_flurry_AuraScript(); + } }; // -31228 - Cheat Death @@ -347,6 +405,58 @@ class spell_rog_prey_on_the_weak : public SpellScriptLoader } }; +// -1943 - Rupture +class spell_rog_rupture : public SpellScriptLoader +{ + public: + spell_rog_rupture() : SpellScriptLoader("spell_rog_rupture") { } + + class spell_rog_rupture_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_rupture_AuraScript); + + bool Load() + { + Unit* caster = GetCaster(); + return caster && caster->GetTypeId() == TYPEID_PLAYER; + } + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated) + { + if (Unit* caster = GetCaster()) + { + canBeRecalculated = false; + + float const attackpowerPerCombo[6] = + { + 0.0f, + 0.015f, // 1 point: ${($m1 + $b1*1 + 0.015 * $AP) * 4} damage over 8 secs + 0.024f, // 2 points: ${($m1 + $b1*2 + 0.024 * $AP) * 5} damage over 10 secs + 0.03f, // 3 points: ${($m1 + $b1*3 + 0.03 * $AP) * 6} damage over 12 secs + 0.03428571f, // 4 points: ${($m1 + $b1*4 + 0.03428571 * $AP) * 7} damage over 14 secs + 0.0375f // 5 points: ${($m1 + $b1*5 + 0.0375 * $AP) * 8} damage over 16 secs + }; + + uint8 cp = caster->ToPlayer()->GetComboPoints(); + if (cp > 5) + cp = 5; + + amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * attackpowerPerCombo[cp]); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_rog_rupture_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_rog_rupture_AuraScript(); + } +}; + // 5938 - Shiv class spell_rog_shiv : public SpellScriptLoader { @@ -388,12 +498,107 @@ class spell_rog_shiv : public SpellScriptLoader } }; +// 57934 - Tricks of the Trade +class spell_rog_tricks_of_the_trade : public SpellScriptLoader +{ + public: + spell_rog_tricks_of_the_trade() : SpellScriptLoader("spell_rog_tricks_of_the_trade") { } + + class spell_rog_tricks_of_the_trade_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_tricks_of_the_trade_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC)) + return false; + return true; + } + + bool Load() + { + _redirectTarget = NULL; + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT) + GetTarget()->ResetRedirectThreat(); + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + _redirectTarget = GetTarget()->GetRedirectThreatTarget(); + return _redirectTarget; + } + + void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + Unit* target = GetTarget(); + target->CastSpell(_redirectTarget, SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST, true); + target->CastSpell(target, SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC, true); + Remove(AURA_REMOVE_BY_DEFAULT); // maybe handle by proc charges + } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_rog_tricks_of_the_trade_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + DoCheckProc += AuraCheckProcFn(spell_rog_tricks_of_the_trade_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_rog_tricks_of_the_trade_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + + private: + Unit* _redirectTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_rog_tricks_of_the_trade_AuraScript(); + } +}; + +// 59628 - Tricks of the Trade (Proc) +class spell_rog_tricks_of_the_trade_proc : public SpellScriptLoader +{ + public: + spell_rog_tricks_of_the_trade_proc() : SpellScriptLoader("spell_rog_tricks_of_the_trade_proc") { } + + class spell_rog_tricks_of_the_trade_proc_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_tricks_of_the_trade_proc_AuraScript); + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->ResetRedirectThreat(); + } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_rog_tricks_of_the_trade_proc_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_rog_tricks_of_the_trade_proc_AuraScript(); + } +}; + void AddSC_rogue_spell_scripts() { + new spell_rog_blade_flurry(); new spell_rog_cheat_death(); new spell_rog_deadly_poison(); new spell_rog_nerves_of_steel(); new spell_rog_preparation(); new spell_rog_prey_on_the_weak(); + new spell_rog_rupture(); new spell_rog_shiv(); + new spell_rog_tricks_of_the_trade(); + new spell_rog_tricks_of_the_trade_proc(); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 0d249953421..b69295150c2 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -33,11 +33,14 @@ enum ShamanSpells SPELL_SHAMAN_ANCESTRAL_AWAKENING_PROC = 52752, SPELL_SHAMAN_BIND_SIGHT = 6277, SPELL_SHAMAN_CLEANSING_TOTEM_EFFECT = 52025, + SPELL_SHAMAN_EARTH_SHIELD_HEAL = 379, SPELL_SHAMAN_EXHAUSTION = 57723, SPELL_SHAMAN_FIRE_NOVA_R1 = 1535, SPELL_SHAMAN_FIRE_NOVA_TRIGGERED_R1 = 8349, + SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD = 63279, SPELL_SHAMAN_GLYPH_OF_HEALING_STREAM_TOTEM = 55456, SPELL_SHAMAN_GLYPH_OF_MANA_TIDE = 55441, + SPELL_SHAMAN_GLYPH_OF_THUNDERSTORM = 62132, SPELL_SHAMAN_LAVA_FLOWS_R1 = 51480, SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 64694, SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE = 52032, @@ -265,6 +268,68 @@ class spell_sha_cleansing_totem_pulse : public SpellScriptLoader } }; +// -974 - Earth Shield +class spell_sha_earth_shield : public SpellScriptLoader +{ + public: + spell_sha_earth_shield() : SpellScriptLoader("spell_sha_earth_shield") { } + + class spell_sha_earth_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_earth_shield_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_EARTH_SHIELD_HEAL)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD)) + return false; + return true; + } + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool & /*canBeRecalculated*/) + { + if (Unit* caster = GetCaster()) + { + amount = caster->SpellHealingBonusDone(GetUnitOwner(), GetSpellInfo(), amount, HEAL); + amount = GetUnitOwner()->SpellHealingBonusTaken(caster, GetSpellInfo(), amount, HEAL); + + // Glyph of Earth Shield + //! WORKAROUND + //! this glyphe is a proc + if (AuraEffect* glyphe = caster->GetAuraEffect(SPELL_SHAMAN_GLYPH_OF_EARTH_SHIELD, EFFECT_0)) + AddPct(amount, glyphe->GetAmount()); + } + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + + //! HACK due to currenct proc system implementation + if (Player* player = GetTarget()->ToPlayer()) + if (player->HasSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL)) + return; + + GetTarget()->CastCustomSpell(SPELL_SHAMAN_EARTH_SHIELD_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), GetTarget(), true, NULL, aurEff, GetCasterGUID()); + + if (Player* player = GetTarget()->ToPlayer()) + player->AddSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, time(NULL) + 3); + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_sha_earth_shield_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_DUMMY); + OnEffectProc += AuraEffectProcFn(spell_sha_earth_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_sha_earth_shield_AuraScript(); + } +}; + // 6474 - Earthbind Totem - Fix Talent: Earthen Power class spell_sha_earthbind_totem : public SpellScriptLoader { @@ -743,6 +808,35 @@ class spell_sha_sentry_totem : public SpellScriptLoader } }; +// -51490 - Thunderstorm +class spell_sha_thunderstorm : public SpellScriptLoader +{ + public: + spell_sha_thunderstorm() : SpellScriptLoader("spell_sha_thunderstorm") { } + + class spell_sha_thunderstorm_SpellScript : public SpellScript + { + PrepareSpellScript(spell_sha_thunderstorm_SpellScript); + + void HandleKnockBack(SpellEffIndex effIndex) + { + // Glyph of Thunderstorm + if (GetCaster()->HasAura(SPELL_SHAMAN_GLYPH_OF_THUNDERSTORM)) + PreventHitDefaultEffect(effIndex); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_sha_thunderstorm_SpellScript::HandleKnockBack, EFFECT_2, SPELL_EFFECT_KNOCK_BACK); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_sha_thunderstorm_SpellScript(); + } +}; + void AddSC_shaman_spell_scripts() { new spell_sha_ancestral_awakening_proc(); @@ -750,6 +844,7 @@ void AddSC_shaman_spell_scripts() new spell_sha_bloodlust(); new spell_sha_chain_heal(); new spell_sha_cleansing_totem_pulse(); + new spell_sha_earth_shield(); new spell_sha_earthbind_totem(); new spell_sha_earthen_power(); new spell_sha_fire_nova(); @@ -760,4 +855,5 @@ void AddSC_shaman_spell_scripts() new spell_sha_mana_spring_totem(); new spell_sha_mana_tide_totem(); new spell_sha_sentry_totem(); + new spell_sha_thunderstorm(); } diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index 89c69733daf..78bfcbab6a0 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -37,6 +37,8 @@ enum WarlockSpells SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELGUARD = 54508, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_FELHUNTER = 54509, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP = 54444, + SPELL_WARLOCK_FEL_SYNERGY_HEAL = 54181, + SPELL_WARLOCK_GLYPHE_OF_SIPHON_LIFE = 63106, SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R1 = 18692, SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R2 = 18693, SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_R1 = 18703, @@ -48,6 +50,7 @@ enum WarlockSpells SPELL_WARLOCK_LIFE_TAP_ENERGIZE = 31818, SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2 = 32553, SPELL_WARLOCK_SOULSHATTER = 32835, + SPELL_WARLOCK_SIPHON_LIFE_HEAL = 63106, SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117 }; @@ -418,6 +421,49 @@ class spell_warl_everlasting_affliction : public SpellScriptLoader } }; +// -47230 - Fel Synergy +class spell_warl_fel_synergy : public SpellScriptLoader +{ + public: + spell_warl_fel_synergy() : SpellScriptLoader("spell_warl_fel_synergy") { } + + class spell_warl_fel_synergy_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_fel_synergy_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_FEL_SYNERGY_HEAL)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return GetTarget()->GetGuardianPet() && eventInfo.GetDamageInfo()->GetDamage(); + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 heal = CalculatePct(int32(eventInfo.GetDamageInfo()->GetDamage()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_WARLOCK_FEL_SYNERGY_HEAL, SPELLVALUE_BASE_POINT0, heal, (Unit*)NULL, true, NULL, aurEff); // TARGET_UNIT_PET + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_warl_fel_synergy_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warl_fel_synergy_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_warl_fel_synergy_AuraScript(); + } +}; + // -48181 - Haunt class spell_warl_haunt : public SpellScriptLoader { @@ -649,6 +695,92 @@ class spell_warl_seed_of_corruption : public SpellScriptLoader } }; +// -7235 - Shadow Ward +class spell_warl_shadow_ward : public SpellScriptLoader +{ + public: + spell_warl_shadow_ward() : SpellScriptLoader("spell_warl_shadow_ward") { } + + class spell_warl_shadow_ward_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_shadow_ward_AuraScript); + + void CalculateAmount(AuraEffect const* /*aurEff*/, int32& amount, bool& canBeRecalculated) + { + canBeRecalculated = false; + if (Unit* caster = GetCaster()) + { + // +80.68% from sp bonus + float bonus = 0.8068f; + + bonus *= caster->SpellBaseHealingBonusDone(GetSpellInfo()->GetSchoolMask()); + bonus *= caster->CalculateLevelPenalty(GetSpellInfo()); + + amount += int32(bonus); + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warl_shadow_ward_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_SCHOOL_ABSORB); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_warl_shadow_ward_AuraScript(); + } +}; + +// 63108 - Siphon Life +class spell_warl_siphon_life : public SpellScriptLoader +{ + public: + spell_warl_siphon_life() : SpellScriptLoader("spell_warl_siphon_life") { } + + class spell_warl_siphon_life_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_siphon_life_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_SIPHON_LIFE_HEAL)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_GLYPHE_OF_SIPHON_LIFE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetDamageInfo()->GetDamage(); + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + int32 amount = int32(CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount())); + // Glyph of Siphon Life + if (AuraEffect const* glyphe = GetTarget()->GetAuraEffect(SPELL_WARLOCK_GLYPHE_OF_SIPHON_LIFE, EFFECT_0)) + AddPct(amount, glyphe->GetAmount()); + + GetTarget()->CastCustomSpell(SPELL_WARLOCK_SIPHON_LIFE_HEAL, SPELLVALUE_BASE_POINT0, amount, GetTarget(), true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_warl_siphon_life_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warl_siphon_life_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_warl_siphon_life_AuraScript(); + } +}; + // 29858 - Soulshatter class spell_warl_soulshatter : public SpellScriptLoader { @@ -737,11 +869,14 @@ void AddSC_warlock_spell_scripts() new spell_warl_demonic_circle_teleport(); new spell_warl_demonic_empowerment(); new spell_warl_everlasting_affliction(); + new spell_warl_fel_synergy(); new spell_warl_haunt(); new spell_warl_health_funnel(); new spell_warl_life_tap(); new spell_warl_ritual_of_doom_effect(); new spell_warl_seed_of_corruption(); + new spell_warl_shadow_ward(); + new spell_warl_siphon_life(); new spell_warl_soulshatter(); new spell_warl_unstable_affliction(); } diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index f84265b89de..7136f046873 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -31,20 +31,26 @@ enum WarriorSpells SPELL_WARRIOR_BLOODTHIRST = 23885, SPELL_WARRIOR_BLOODTHIRST_DAMAGE = 23881, SPELL_WARRIOR_CHARGE = 34846, + SPELL_WARRIOR_DAMAGE_SHIELD_DAMAGE = 59653, SPELL_WARRIOR_DEEP_WOUNDS_RANK_1 = 12162, SPELL_WARRIOR_DEEP_WOUNDS_RANK_2 = 12850, SPELL_WARRIOR_DEEP_WOUNDS_RANK_3 = 12868, SPELL_WARRIOR_DEEP_WOUNDS_RANK_PERIODIC = 12721, SPELL_WARRIOR_EXECUTE = 20647, SPELL_WARRIOR_GLYPH_OF_EXECUTION = 58367, + SPELL_WARRIOR_GLYPH_OF_VIGILANCE = 63326, SPELL_WARRIOR_JUGGERNAUT_CRIT_BONUS_BUFF = 65156, SPELL_WARRIOR_JUGGERNAUT_CRIT_BONUS_TALENT = 64976, SPELL_WARRIOR_LAST_STAND_TRIGGERED = 12976, SPELL_WARRIOR_SLAM = 50783, + SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK = 26654, + SPELL_WARRIOR_TAUNT = 355, SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_1 = 46859, SPELL_WARRIOR_UNRELENTING_ASSAULT_RANK_2 = 46860, SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_1 = 64849, SPELL_WARRIOR_UNRELENTING_ASSAULT_TRIGGER_2 = 64850, + SPELL_WARRIOR_VIGILANCE_PROC = 50725, + SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT = 59665, SPELL_PALADIN_BLESSING_OF_SANCTUARY = 20911, SPELL_PALADIN_GREATER_BLESSING_OF_SANCTUARY = 25899, @@ -194,6 +200,44 @@ class spell_warr_concussion_blow : public SpellScriptLoader } }; +// -58872 - Damage Shield +class spell_warr_damage_shield : public SpellScriptLoader +{ + public: + spell_warr_damage_shield() : SpellScriptLoader("spell_warr_damage_shield") { } + + class spell_warr_damage_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_damage_shield_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_DAMAGE_SHIELD_DAMAGE)) + return false; + return true; + } + + void OnProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + // % of amount blocked + int32 damage = CalculatePct(int32(GetTarget()->GetShieldBlockValue()), aurEff->GetAmount()); + GetTarget()->CastCustomSpell(SPELL_WARRIOR_DAMAGE_SHIELD_DAMAGE, SPELLVALUE_BASE_POINT0, damage, eventInfo.GetProcTarget(), true, NULL, aurEff); + } + + void Register() + { + OnEffectProc += AuraEffectProcFn(spell_warr_damage_shield_AuraScript::OnProc, EFFECT_0, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_warr_damage_shield_AuraScript(); + } +}; + // -12162 - Deep Wounds class spell_warr_deep_wounds : public SpellScriptLoader { @@ -333,6 +377,34 @@ class spell_warr_improved_spell_reflection : public SpellScriptLoader } }; +// 5246 - Intimidating Shout +class spell_warr_intimidating_shout : public SpellScriptLoader +{ + public: + spell_warr_intimidating_shout() : SpellScriptLoader("spell_warr_intimidating_shout") { } + + class spell_warr_intimidating_shout_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warr_intimidating_shout_SpellScript); + + void FilterTargets(std::list<WorldObject*>& unitList) + { + unitList.remove(GetExplTargetWorldObject()); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_intimidating_shout_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_warr_intimidating_shout_SpellScript::FilterTargets, EFFECT_2, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_warr_intimidating_shout_SpellScript(); + } +}; + // 12975 - Last Stand class spell_warr_last_stand : public SpellScriptLoader { @@ -409,6 +481,83 @@ class spell_warr_overpower : public SpellScriptLoader } }; +// -772 - Rend +class spell_warr_rend : public SpellScriptLoader +{ + public: + spell_warr_rend() : SpellScriptLoader("spell_warr_rend") { } + + class spell_warr_rend_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_rend_AuraScript); + + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) + { + if (Unit* caster = GetCaster()) + { + canBeRecalculated = false; + + // $0.2 * (($MWB + $mwb) / 2 + $AP / 14 * $MWS) bonus per tick + float ap = caster->GetTotalAttackPowerValue(BASE_ATTACK); + int32 mws = caster->GetAttackTime(BASE_ATTACK); + float mwbMin = caster->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE); + float mwbMax = caster->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE); + float mwb = ((mwbMin + mwbMax) / 2 + ap * mws / 14000) * 0.2f; + amount += int32(caster->ApplyEffectModifiers(GetSpellInfo(), aurEff->GetEffIndex(), mwb)); + + // "If used while your target is above 75% health, Rend does 35% more damage." + // as for 3.1.3 only ranks above 9 (wrong tooltip?) + if (GetSpellInfo()->GetRank() >= 9) + { + if (GetUnitOwner()->HasAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, GetSpellInfo(), caster)) + AddPct(amount, GetSpellInfo()->Effects[EFFECT_2].CalcValue(caster)); + } + } + } + + void Register() + { + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_warr_rend_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_warr_rend_AuraScript(); + } +}; + +// 64380, 65941 - Shattering Throw +class spell_warr_shattering_throw : public SpellScriptLoader +{ + public: + spell_warr_shattering_throw() : SpellScriptLoader("spell_warr_shattering_throw") { } + + class spell_warr_shattering_throw_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warr_shattering_throw_SpellScript); + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + // remove shields, will still display immune to damage part + if (Unit* target = GetHitUnit()) + target->RemoveAurasWithMechanic(1 << MECHANIC_IMMUNE_SHIELD, AURA_REMOVE_BY_ENEMY_SPELL); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_warr_shattering_throw_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_warr_shattering_throw_SpellScript(); + } +}; + // -1464 - Slam class spell_warr_slam : public SpellScriptLoader { @@ -445,6 +594,57 @@ class spell_warr_slam : public SpellScriptLoader } }; +// 12328, 18765, 35429 - Sweeping Strikes +class spell_warr_sweeping_strikes : public SpellScriptLoader +{ + public: + spell_warr_sweeping_strikes() : SpellScriptLoader("spell_warr_sweeping_strikes") { } + + class spell_warr_sweeping_strikes_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warr_sweeping_strikes_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK)) + return false; + return true; + } + + bool Load() + { + _procTarget = NULL; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + _procTarget = eventInfo.GetActor()->SelectNearbyTarget(eventInfo.GetProcTarget()); + return _procTarget; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(_procTarget, SPELL_WARRIOR_SWEEPING_STRIKES_EXTRA_ATTACK, true, NULL, aurEff); + } + + void Register() + { + DoCheckProc += AuraCheckProcFn(spell_warr_sweeping_strikes_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warr_sweeping_strikes_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + private: + Unit* _procTarget; + }; + + AuraScript* GetAuraScript() const + { + return new spell_warr_sweeping_strikes_AuraScript(); + } +}; + // 50720 - Vigilance class spell_warr_vigilance : public SpellScriptLoader { @@ -457,34 +657,80 @@ class spell_warr_vigilance : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) { + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_GLYPH_OF_VIGILANCE)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_VIGILANCE_PROC)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT)) + return false; if (!sSpellMgr->GetSpellInfo(SPELL_GEN_DAMAGE_REDUCTION_AURA)) return false; return true; } - void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + bool Load() + { + _procTarget = NULL; + return true; + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_GEN_DAMAGE_REDUCTION_AURA, true); + + if (Unit* caster = GetCaster()) + target->CastSpell(caster, SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT, true); + } + + void HandleAfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* target = GetTarget()) - target->CastSpell(target, SPELL_GEN_DAMAGE_REDUCTION_AURA, true); + //! WORKAROUND + //! this glyph is a proc + if (Unit* caster = GetCaster()) + { + if (AuraEffect const* glyph = caster->GetAuraEffect(SPELL_WARRIOR_GLYPH_OF_VIGILANCE, EFFECT_0)) + GetTarget()->ModifyRedirectThreat(glyph->GetAmount()); + } } - void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* target = GetTarget()) + Unit* target = GetTarget(); + if (target->HasAura(SPELL_GEN_DAMAGE_REDUCTION_AURA) && + !(target->HasAura(SPELL_PALADIN_BLESSING_OF_SANCTUARY) || + target->HasAura(SPELL_PALADIN_GREATER_BLESSING_OF_SANCTUARY) || + target->HasAura(SPELL_PRIEST_RENEWED_HOPE))) { - if (target->HasAura(SPELL_GEN_DAMAGE_REDUCTION_AURA) && !(target->HasAura(SPELL_PALADIN_BLESSING_OF_SANCTUARY) || - target->HasAura(SPELL_PALADIN_GREATER_BLESSING_OF_SANCTUARY) || - target->HasAura(SPELL_PRIEST_RENEWED_HOPE))) - target->RemoveAurasDueToSpell(SPELL_GEN_DAMAGE_REDUCTION_AURA); + target->RemoveAurasDueToSpell(SPELL_GEN_DAMAGE_REDUCTION_AURA); } + + target->ResetRedirectThreat(); + } + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + _procTarget = GetCaster(); + return _procTarget; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) + { + PreventDefaultAction(); + GetTarget()->CastSpell(_procTarget, SPELL_WARRIOR_VIGILANCE_PROC, true, NULL, aurEff); } void Register() { - OnEffectApply += AuraEffectApplyFn(spell_warr_vigilance_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - OnEffectRemove += AuraEffectRemoveFn(spell_warr_vigilance_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectApply += AuraEffectApplyFn(spell_warr_vigilance_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + AfterEffectApply += AuraEffectApplyFn(spell_warr_vigilance_AuraScript::HandleAfterApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + OnEffectRemove += AuraEffectRemoveFn(spell_warr_vigilance_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + DoCheckProc += AuraCheckProcFn(spell_warr_vigilance_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warr_vigilance_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); } + private: + Unit* _procTarget; }; AuraScript* GetAuraScript() const @@ -493,17 +739,54 @@ class spell_warr_vigilance : public SpellScriptLoader } }; +// 50725 Vigilance +class spell_warr_vigilance_trigger : public SpellScriptLoader +{ + public: + spell_warr_vigilance_trigger() : SpellScriptLoader("spell_warr_vigilance_trigger") { } + + class spell_warr_vigilance_trigger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warr_vigilance_trigger_SpellScript); + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + + // Remove Taunt cooldown + if (Player* target = GetHitPlayer()) + target->RemoveSpellCooldown(SPELL_WARRIOR_TAUNT, true); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_warr_vigilance_trigger_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_warr_vigilance_trigger_SpellScript(); + } +}; + void AddSC_warrior_spell_scripts() { new spell_warr_bloodthirst(); new spell_warr_bloodthirst_heal(); new spell_warr_charge(); new spell_warr_concussion_blow(); + new spell_warr_damage_shield(); new spell_warr_deep_wounds(); new spell_warr_execute(); new spell_warr_improved_spell_reflection(); + new spell_warr_intimidating_shout(); new spell_warr_last_stand(); new spell_warr_overpower(); + new spell_warr_rend(); + new spell_warr_shattering_throw(); new spell_warr_slam(); + new spell_warr_sweeping_strikes(); new spell_warr_vigilance(); + new spell_warr_vigilance_trigger(); } diff --git a/src/server/shared/CMakeLists.txt b/src/server/shared/CMakeLists.txt index 86d0cbf613b..01ccf648b31 100644 --- a/src/server/shared/CMakeLists.txt +++ b/src/server/shared/CMakeLists.txt @@ -50,6 +50,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/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h index 34b7c5083e3..9f93e99ab26 100644 --- a/src/server/shared/Database/DatabaseWorkerPool.h +++ b/src/server/shared/Database/DatabaseWorkerPool.h @@ -31,6 +31,9 @@ #include "QueryHolder.h" #include "AdhocStatement.h" +#define MIN_MYSQL_SERVER_VERSION 50100u +#define MIN_MYSQL_CLIENT_VERSION 50100u + class PingOperation : public SQLOperation { //! Operation for idle delaythreads @@ -53,6 +56,7 @@ class DatabaseWorkerPool _connections.resize(IDX_SIZE); WPFatal (mysql_thread_safe(), "Used MySQL library isn't thread-safe."); + WPFatal (mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1"); } ~DatabaseWorkerPool() @@ -73,6 +77,7 @@ class DatabaseWorkerPool { T* t = new T(_queue, _connectionInfo); res &= t->Open(); + WPFatal (mysql_get_server_version(t->GetHandle()) >= MIN_MYSQL_SERVER_VERSION, "TrinityCore does not support MySQL versions below 5.1"); _connections[IDX_ASYNC][i] = t; ++_connectionCount[IDX_ASYNC]; } diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index cfbd4b2bc45..1c2410b8b53 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -374,7 +374,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_GROUP_DIFFICULTY, "UPDATE groups SET difficulty = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY, "UPDATE groups SET raiddifficulty = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_tickets", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_INVALID_SPELL, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_INVALID_SPELL_TALENTS, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_INVALID_SPELL_SPELLS, "DELETE FROM character_spell WHERE spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = '', account = 0 WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UDP_RESTORE_DELETE_INFO, "UPDATE characters SET name = ?, account = ?, deleteDate = NULL, deleteInfos_Name = NULL, deleteInfos_Account = NULL WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ZONE, "UPDATE characters SET zone = ? WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index bfa7bc48cf5..e4728e19934 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -315,7 +315,8 @@ enum CharacterDatabaseStatements CHAR_UPD_GROUP_MEMBER_FLAG, CHAR_UPD_GROUP_DIFFICULTY, CHAR_UPD_GROUP_RAID_DIFFICULTY, - CHAR_DEL_INVALID_SPELL, + CHAR_DEL_INVALID_SPELL_SPELLS, + CHAR_DEL_INVALID_SPELL_TALENTS, CHAR_UPD_DELETE_INFO, CHAR_UDP_RESTORE_DELETE_INFO, CHAR_UPD_ZONE, diff --git a/src/server/shared/Debugging/Errors.h b/src/server/shared/Debugging/Errors.h index 3d10740f149..10e94634e9a 100644 --- a/src/server/shared/Debugging/Errors.h +++ b/src/server/shared/Debugging/Errors.h @@ -24,7 +24,7 @@ #include <ace/Stack_Trace.h> #include <ace/OS_NS_unistd.h> -#define WPAssert(assertion) { if (!(assertion)) { ACE_Stack_Trace st; sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } } +#define WPAssert(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } } #define WPError(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); *((volatile int*)NULL) = 0; } } #define WPWarning(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s WARNING:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); } } #define WPFatal(assertion, errmsg) { if (!(assertion)) { sLog->outError(LOG_FILTER_GENERAL, "\n%s:%i in %s FATAL ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg); ACE_OS::sleep(10); *((volatile int*)NULL) = 0; } } diff --git a/src/server/shared/Logging/Appender.cpp b/src/server/shared/Logging/Appender.cpp index f2f5da53dae..1d215e1212e 100644 --- a/src/server/shared/Logging/Appender.cpp +++ b/src/server/shared/Logging/Appender.cpp @@ -73,10 +73,7 @@ void Appender::setLogLevel(LogLevel _level) void Appender::write(LogMessage& message) { if (!level || level > message.level) - { - //fprintf(stderr, "Appender::write: Appender %s, Level %s. Msg %s Level %s Type %s WRONG LEVEL MASK\n", getName().c_str(), getLogLevelString(level), message.text.c_str(), getLogLevelString(message.level), getLogFilterTypeString(message.type)); // DEBUG - RemoveMe return; - } message.prefix.clear(); if (flags & APPENDER_FLAGS_PREFIX_TIMESTAMP) @@ -222,5 +219,6 @@ char const* Appender::getLogFilterTypeString(LogFilterType type) default: break; } + return "???"; } diff --git a/src/server/shared/Logging/Appender.h b/src/server/shared/Logging/Appender.h index 6a0f0bdac26..08628948b90 100644 --- a/src/server/shared/Logging/Appender.h +++ b/src/server/shared/Logging/Appender.h @@ -123,6 +123,12 @@ struct LogMessage std::string prefix; std::string param1; time_t mtime; + + ///@ Returns size of the log message content in bytes + uint32 Size() const + { + return prefix.size() + text.size(); + } }; class Appender @@ -143,7 +149,7 @@ class Appender static const char* getLogFilterTypeString(LogFilterType type); private: - virtual void _write(LogMessage& /*message*/) = 0; + virtual void _write(LogMessage const& /*message*/) = 0; uint8 id; std::string name; diff --git a/src/server/shared/Logging/AppenderConsole.cpp b/src/server/shared/Logging/AppenderConsole.cpp index d0af761188c..a1212bd135b 100644 --- a/src/server/shared/Logging/AppenderConsole.cpp +++ b/src/server/shared/Logging/AppenderConsole.cpp @@ -154,7 +154,7 @@ void AppenderConsole::ResetColor(bool stdout_stream) #endif } -void AppenderConsole::_write(LogMessage& message) +void AppenderConsole::_write(LogMessage const& message) { bool stdout_stream = message.level == LOG_LEVEL_ERROR || message.level == LOG_LEVEL_FATAL; diff --git a/src/server/shared/Logging/AppenderConsole.h b/src/server/shared/Logging/AppenderConsole.h index 3319c84e887..6f3fcca901c 100644 --- a/src/server/shared/Logging/AppenderConsole.h +++ b/src/server/shared/Logging/AppenderConsole.h @@ -51,7 +51,7 @@ class AppenderConsole: public Appender private: void SetColor(bool stdout_stream, ColorTypes color); void ResetColor(bool stdout_stream); - void _write(LogMessage& message); + void _write(LogMessage const& message); bool _colored; ColorTypes _colors[MaxLogLevels]; }; diff --git a/src/server/shared/Logging/AppenderDB.cpp b/src/server/shared/Logging/AppenderDB.cpp index 86677eeedd8..ae5fc17de73 100644 --- a/src/server/shared/Logging/AppenderDB.cpp +++ b/src/server/shared/Logging/AppenderDB.cpp @@ -18,8 +18,8 @@ #include "AppenderDB.h" #include "Database/DatabaseEnv.h" -AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level, uint32 realmId) - : Appender(id, name, APPENDER_DB, level), realm(realmId), enable(false) +AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level) + : Appender(id, name, APPENDER_DB, level), realmId(0), enabled(false) { } @@ -27,10 +27,11 @@ AppenderDB::~AppenderDB() { } -void AppenderDB::_write(LogMessage& message) +void AppenderDB::_write(LogMessage const& message) { - if (!enable) + if (!enabled) return; + switch (message.type) { case LOG_FILTER_SQL: @@ -40,7 +41,7 @@ void AppenderDB::_write(LogMessage& message) default: PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG); stmt->setUInt64(0, message.mtime); - stmt->setUInt32(1, realm); + stmt->setUInt32(1, realmId); stmt->setUInt8(2, uint8(message.type)); stmt->setUInt8(3, uint8(message.level)); stmt->setString(4, message.text); @@ -49,7 +50,8 @@ void AppenderDB::_write(LogMessage& message) } } -void AppenderDB::setEnable(bool _enable) +void AppenderDB::setRealmId(uint32 _realmId) { - enable = _enable; + enabled = true; + realmId = _realmId; } diff --git a/src/server/shared/Logging/AppenderDB.h b/src/server/shared/Logging/AppenderDB.h index 5ab9a1ee423..f9dde0a1e82 100644 --- a/src/server/shared/Logging/AppenderDB.h +++ b/src/server/shared/Logging/AppenderDB.h @@ -23,14 +23,15 @@ class AppenderDB: public Appender { public: - AppenderDB(uint8 _id, std::string const& _name, LogLevel level, uint32 realmId); + AppenderDB(uint8 _id, std::string const& _name, LogLevel level); ~AppenderDB(); - void setEnable(bool enable); + + void setRealmId(uint32 realmId); private: - uint32 realm; - bool enable; - void _write(LogMessage& message); + uint32 realmId; + bool enabled; + void _write(LogMessage const& message); }; #endif diff --git a/src/server/shared/Logging/AppenderFile.cpp b/src/server/shared/Logging/AppenderFile.cpp index 7b0bac03d03..1ed6350f813 100644 --- a/src/server/shared/Logging/AppenderFile.cpp +++ b/src/server/shared/Logging/AppenderFile.cpp @@ -18,57 +18,75 @@ #include "AppenderFile.h" #include "Common.h" -AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, const char* _filename, const char* _logDir, const char* _mode, AppenderFlags _flags) - : Appender(id, name, APPENDER_FILE, level, _flags) - , filename(_filename) - , logDir(_logDir) - , mode(_mode) +AppenderFile::AppenderFile(uint8 id, std::string const& name, LogLevel level, const char* _filename, const char* _logDir, const char* _mode, AppenderFlags _flags, uint64 fileSize): + Appender(id, name, APPENDER_FILE, level, _flags), + filename(_filename), + logDir(_logDir), + mode(_mode), + maxFileSize(fileSize), + fileSize(0) { dynamicName = std::string::npos != filename.find("%s"); backup = _flags & APPENDER_FLAGS_MAKE_FILE_BACKUP; - logfile = !dynamicName ? OpenFile(_filename, _mode, backup) : NULL; + logfile = !dynamicName ? OpenFile(_filename, _mode, mode == "w" && backup) : NULL; } AppenderFile::~AppenderFile() { - if (logfile) - { - fclose(logfile); - logfile = NULL; - } + CloseFile(); } -void AppenderFile::_write(LogMessage& message) +void AppenderFile::_write(LogMessage const& message) { + bool exceedMaxSize = maxFileSize > 0 && (fileSize + message.Size()) > maxFileSize; + if (dynamicName) { char namebuf[TRINITY_PATH_MAX]; snprintf(namebuf, TRINITY_PATH_MAX, filename.c_str(), message.param1.c_str()); - logfile = OpenFile(namebuf, mode, backup); + logfile = OpenFile(namebuf, mode, backup || exceedMaxSize); } + else if (exceedMaxSize) + logfile = OpenFile(filename, "w", true); - if (logfile) - { - fprintf(logfile, "%s%s", message.prefix.c_str(), message.text.c_str()); - fflush(logfile); + if (!logfile) + return; - if (dynamicName) - { - fclose(logfile); - logfile = NULL; - } - } + fprintf(logfile, "%s%s", message.prefix.c_str(), message.text.c_str()); + fflush(logfile); + fileSize += message.Size(); + + if (dynamicName) + CloseFile(); } FILE* AppenderFile::OpenFile(std::string const &filename, std::string const &mode, bool backup) { - if (mode == "w" && backup) + std::string fullName(logDir + filename); + if (backup) { - std::string newName(filename); + CloseFile(); + std::string newName(fullName); newName.push_back('.'); newName.append(LogMessage::getTimeStr(time(NULL))); - rename(filename.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore + rename(fullName.c_str(), newName.c_str()); // no error handling... if we couldn't make a backup, just ignore + } + + if (FILE* ret = fopen(fullName.c_str(), mode.c_str())) + { + fileSize = ftell(ret); + return ret; + } + + return NULL; +} + +void AppenderFile::CloseFile() +{ + if (logfile) + { + fclose(logfile); + logfile = NULL; } - return fopen((logDir + filename).c_str(), mode.c_str()); } diff --git a/src/server/shared/Logging/AppenderFile.h b/src/server/shared/Logging/AppenderFile.h index 934370d70b4..c15974799e1 100644 --- a/src/server/shared/Logging/AppenderFile.h +++ b/src/server/shared/Logging/AppenderFile.h @@ -23,18 +23,21 @@ class AppenderFile: public Appender { public: - AppenderFile(uint8 _id, std::string const& _name, LogLevel level, const char* filename, const char* logDir, const char* mode, AppenderFlags flags); + AppenderFile(uint8 _id, std::string const& _name, LogLevel level, const char* filename, const char* logDir, const char* mode, AppenderFlags flags, uint64 maxSize); ~AppenderFile(); FILE* OpenFile(std::string const& _name, std::string const& _mode, bool _backup); private: - void _write(LogMessage& message); + void CloseFile(); + void _write(LogMessage const& message); FILE* logfile; std::string filename; std::string logDir; std::string mode; bool dynamicName; bool backup; + uint64 maxFileSize; + uint64 fileSize; }; #endif diff --git a/src/server/shared/Logging/Log.cpp b/src/server/shared/Logging/Log.cpp index 60320d049ac..f5ef6dfcc5b 100644 --- a/src/server/shared/Logging/Log.cpp +++ b/src/server/shared/Logging/Log.cpp @@ -31,7 +31,6 @@ Log::Log() : worker(NULL) { - SetRealmID(0); m_logsTimestamp = "_" + GetTimestampStr(); LoadFromConfig(); } @@ -90,8 +89,9 @@ void Log::CreateAppenderFromConfig(const char* name) options = ConfigMgr::GetStringDefault(options.c_str(), ""); Tokenizer tokens(options, ','); Tokenizer::const_iterator iter = tokens.begin(); + uint8 size = tokens.size(); - if (tokens.size() < 2) + if (size < 2) { fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong configuration for appender %s. Config line: %s\n", name, options.c_str()); return; @@ -99,16 +99,15 @@ void Log::CreateAppenderFromConfig(const char* name) AppenderFlags flags = APPENDER_FLAGS_NONE; AppenderType type = AppenderType(atoi(*iter)); - ++iter; - LogLevel level = LogLevel(atoi(*iter)); + LogLevel level = LogLevel(atoi(*(++iter))); if (level > LOG_LEVEL_FATAL) { fprintf(stderr, "Log::CreateAppenderFromConfig: Wrong Log Level %d for appender %s\n", level, name); return; } - if (++iter != tokens.end()) - flags = AppenderFlags(atoi(*iter)); + if (size > 2) + flags = AppenderFlags(atoi(*(++iter))); switch (type) { @@ -116,7 +115,7 @@ void Log::CreateAppenderFromConfig(const char* name) { AppenderConsole* appender = new AppenderConsole(NextAppenderId(), name, level, flags); appenders[appender->getId()] = appender; - if (++iter != tokens.end()) + if (iter != tokens.end()) appender->InitColors(*iter); //fprintf(stdout, "Log::CreateAppenderFromConfig: Created Appender %s (%u), Type CONSOLE, Mask %u\n", appender->getName().c_str(), appender->getId(), appender->getLogLevel()); // DEBUG - RemoveMe break; @@ -126,16 +125,16 @@ void Log::CreateAppenderFromConfig(const char* name) std::string filename; std::string mode = "a"; - if (++iter == tokens.end()) + if (size < 4) { fprintf(stderr, "Log::CreateAppenderFromConfig: Missing file name for appender %s\n", name); return; } - filename = *iter; + filename = *(++iter); - if (++iter != tokens.end()) - mode = *iter; + if (size > 4) + mode = *(++iter); if (flags & APPENDER_FLAGS_USE_TIMESTAMP) { @@ -146,15 +145,19 @@ void Log::CreateAppenderFromConfig(const char* name) filename += m_logsTimestamp; } + uint64 maxFileSize = 0; + if (size > 5) + maxFileSize = atoi(*(++iter)); + uint8 id = NextAppenderId(); - appenders[id] = new AppenderFile(id, name, level, filename.c_str(), m_logsDir.c_str(), mode.c_str(), flags); + appenders[id] = new AppenderFile(id, name, level, filename.c_str(), m_logsDir.c_str(), mode.c_str(), flags, maxFileSize); //fprintf(stdout, "Log::CreateAppenderFromConfig: Created Appender %s (%u), Type FILE, Mask %u, File %s, Mode %s\n", name, id, level, filename.c_str(), mode.c_str()); // DEBUG - RemoveMe break; } case APPENDER_DB: { uint8 id = NextAppenderId(); - appenders[id] = new AppenderDB(id, name, level, realm); + appenders[id] = new AppenderDB(id, name, level); break; } default: @@ -265,13 +268,6 @@ void Log::ReadLoggersFromConfig() loggers[LOG_FILTER_GENERAL].Create("root", LOG_FILTER_GENERAL, LOG_LEVEL_DISABLED); } -void Log::EnableDBAppenders() -{ - for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it) - if (it->second && it->second->getType() == APPENDER_DB) - ((AppenderDB *)it->second)->setEnable(true); -} - void Log::vlog(LogFilterType filter, LogLevel level, char const* str, va_list argptr) { char text[MAX_QUERY_LEN]; @@ -281,12 +277,16 @@ void Log::vlog(LogFilterType filter, LogLevel level, char const* str, va_list ar void Log::write(LogMessage* msg) { + if (loggers.empty()) + return; + + msg->text.append("\n"); + Logger* logger = GetLoggerByType(msg->type); + if (worker) - { - msg->text.append("\n"); - Logger* logger = GetLoggerByType(msg->type); worker->enqueue(new LogOperation(logger, msg)); - } + else + logger->write(*msg); } std::string Log::GetTimestampStr() @@ -463,9 +463,11 @@ void Log::outCommand(uint32 account, const char * str, ...) write(msg); } -void Log::SetRealmID(uint32 id) +void Log::SetRealmId(uint32 id) { - realm = id; + for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it) + if (it->second && it->second->getType() == APPENDER_DB) + ((AppenderDB *)it->second)->setRealmId(id); } void Log::Close() @@ -484,7 +486,10 @@ void Log::Close() void Log::LoadFromConfig() { Close(); - worker = new LogWorker(); + + if (ConfigMgr::GetBoolDefault("Log.Async.Enable", false)) + worker = new LogWorker(); + AppenderId = 0; m_logsDir = ConfigMgr::GetStringDefault("LogsDir", ""); if (!m_logsDir.empty()) diff --git a/src/server/shared/Logging/Log.h b/src/server/shared/Logging/Log.h index 6d6cfe715e8..46aaea4bad1 100644 --- a/src/server/shared/Logging/Log.h +++ b/src/server/shared/Logging/Log.h @@ -52,12 +52,11 @@ class Log void outError(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4); void outFatal(LogFilterType f, char const* str, ...) ATTR_PRINTF(3, 4); - void EnableDBAppenders(); void outCommand(uint32 account, const char * str, ...) ATTR_PRINTF(3, 4); void outCharDump(char const* str, uint32 account_id, uint32 guid, char const* name); static std::string GetTimestampStr(); - void SetRealmID(uint32 id); + void SetRealmId(uint32 id); private: void vlog(LogFilterType f, LogLevel level, char const* str, va_list argptr); @@ -78,7 +77,6 @@ class Log std::string m_logsDir; std::string m_logsTimestamp; - uint32 realm; LogWorker* worker; }; diff --git a/src/server/shared/Memory.h b/src/server/shared/Memory.h new file mode 100644 index 00000000000..25533638915 --- /dev/null +++ b/src/server/shared/Memory.h @@ -0,0 +1,19 @@ + + +#ifndef _MEMORY_H +#define _MEMORY_H + +#include "DetourAlloc.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 413d6487480..8c1350a7ca0 100644 --- a/src/server/worldserver/CMakeLists.txt +++ b/src/server/worldserver/CMakeLists.txt @@ -39,6 +39,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 @@ -163,6 +164,7 @@ target_link_libraries(worldserver collision g3dlib gsoap + Detour ${JEMALLOC_LIBRARY} ${READLINE_LIBRARY} ${TERMCAP_LIBRARY} diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp index fe872c1977f..56dd64bc45a 100644 --- a/src/server/worldserver/Master.cpp +++ b/src/server/worldserver/Master.cpp @@ -438,8 +438,6 @@ bool Master::_StartDB() } sLog->outInfo(LOG_FILTER_WORLDSERVER, "Realm running as realm ID %d", realmID); - sLog->SetRealmID(realmID); - ///- Clean the database before starting ClearOnlineAccounts(); diff --git a/src/server/worldserver/RemoteAccess/RASocket.cpp b/src/server/worldserver/RemoteAccess/RASocket.cpp index 75c95d5ea98..e0f4e7f0de6 100644 --- a/src/server/worldserver/RemoteAccess/RASocket.cpp +++ b/src/server/worldserver/RemoteAccess/RASocket.cpp @@ -65,7 +65,13 @@ int RASocket::handle_close(ACE_HANDLE, ACE_Reactor_Mask) int RASocket::send(const std::string& line) { - return size_t(peer().send(line.c_str(), line.length())) == line.length() ? 0 : -1; +#ifdef MSG_NOSIGNAL + ssize_t n = peer().send(line.c_str(), line.length(), MSG_NOSIGNAL); +#else + ssize_t n = peer().send(line.c_str(), line.length()); +#endif // MSG_NOSIGNAL + + return n == ssize_t(line.length()) ? 0 : -1; } int RASocket::recv_line(ACE_Message_Block& buffer) @@ -160,7 +166,7 @@ int RASocket::process_command(const std::string& command) break; } - if (size_t(peer().send(mb->rd_ptr(), mb->length())) != mb->length()) + if (send(std::string(mb->rd_ptr(), mb->length())) == -1) { mb->release(); return -1; @@ -356,8 +362,7 @@ int RASocket::svc(void) for (;;) { // show prompt - const char* tc_prompt = "TC> "; - if (size_t(peer().send(tc_prompt, strlen(tc_prompt))) != strlen(tc_prompt)) + if (send("TC> ") == -1) return -1; std::string line; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 68cf3994e3b..31d180df751 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -269,6 +269,14 @@ PlayerSave.Stats.MinLevel = 0 PlayerSave.Stats.SaveOnlyOnLogout = 1 # +# mmap.enablePathFinding +# Description: Enable/Disable pathfinding using mmaps - experimental +# Default: 0 - (Disabled) +# 1 - (Enabled) + +mmap.enablePathFinding = 0 + +# # vmap.enableLOS # vmap.enableHeight # Description: VMmap support for line of sight and height calculation. @@ -280,14 +288,6 @@ vmap.enableLOS = 1 vmap.enableHeight = 1 # -# vmap.petLOS -# Description: Check line of sight for pets, to avoid them attacking through walls. -# Default: 1 - (Enabled, each pet attack will be checked for line of sight) -# 0 - (Disabled, somewhat less CPU usage) - -vmap.petLOS = 1 - -# # vmap.enableIndoorCheck # Description: VMap based indoor check to remove outdoor-only auras (mounts etc.). # Default: 1 - (Enabled) @@ -2661,7 +2661,7 @@ PlayerDump.DisallowOverwrite = 1 # Appender config values: Given a appender "name" # Appender.name # Description: Defines 'where to log' -# Format: Type,LogLevel,Flags,optional1,optional2 +# Format: Type,LogLevel,Flags,optional1,optional2,optional3 # # Type # 0 - (None) @@ -2712,6 +2712,13 @@ PlayerDump.DisallowOverwrite = 1 # a - (Append) # w - (Overwrite) # +# MaxFileSize: Maximum file size of the log file before creating a new log file +# (read as optional3 if Type = File) +# Size is measured in bytes expressed in a 64-bit unsigned integer. +# Maximum value is 4294967295 (4 gb). Leave blank for no limit. +# NOTE: Does not work with dynamic filenames. +# Example: 536870912 (512 mb) +# Appender.Console=1,3,0 Appender.Server=2,2,0,Server.log,w @@ -2824,4 +2831,12 @@ Logger.Opcodes=41,6,Console Server Loggers=Root Chat DBErrors GM RA Warden Character Load WorldServer Opcodes # +# Log.Async.Enable +# Description: Enables asyncronous message logging. +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Log.Async.Enable = 0 + +# ################################################################################################### diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 0386f27d3df..49c17913c64 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -11,3 +11,5 @@ add_subdirectory(map_extractor) add_subdirectory(vmap4_assembler) add_subdirectory(vmap4_extractor) +add_subdirectory(mmaps_generator) +add_subdirectory(mesh_extractor) diff --git a/src/tools/map_extractor/CMakeLists.txt b/src/tools/map_extractor/CMakeLists.txt index 9fb8d5713d7..ea073456680 100644 --- a/src/tools/map_extractor/CMakeLists.txt +++ b/src/tools/map_extractor/CMakeLists.txt @@ -11,23 +11,22 @@ file(GLOB_RECURSE sources *.cpp *.h) -if( UNIX ) - include_directories ( +set(include_Dirs ${CMAKE_SOURCE_DIR}/src/server/shared ${CMAKE_SOURCE_DIR}/dep/libmpq ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/loadlib - ) -elseif( WIN32 ) - include_directories ( - ${CMAKE_SOURCE_DIR}/src/server/shared - ${CMAKE_SOURCE_DIR}/dep/libmpq +) + +if( WIN32 ) + set(include_Dirs + ${include_Dirs} ${CMAKE_SOURCE_DIR}/dep/libmpq/win - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/loadlib ) endif() +include_directories(${include_Dirs}) + add_executable(mapextractor ${sources} ) @@ -38,8 +37,6 @@ target_link_libraries(mapextractor ${ZLIB_LIBRARIES} ) -add_dependencies(mapextractor mpq) - if( UNIX ) install(TARGETS mapextractor DESTINATION bin) elseif( WIN32 ) diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index d0342717324..bf88a92c32a 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -222,7 +222,7 @@ uint32 ReadMapDBC() map_ids[x].id = dbc.getRecord(x).getUInt(0); strcpy(map_ids[x].name, dbc.getRecord(x).getString(1)); } - printf("Done! (%zu maps loaded)\n", map_count); + printf("Done! (%u maps loaded)\n", (uint32)map_count); return map_count; } @@ -247,7 +247,7 @@ void ReadAreaTableDBC() maxAreaId = dbc.getMaxId(); - printf("Done! (%zu areas loaded)\n", area_count); + printf("Done! (%u areas loaded)\n", (uint32)area_count); } void ReadLiquidTypeTableDBC() @@ -268,7 +268,7 @@ void ReadLiquidTypeTableDBC() for(uint32 x = 0; x < liqTypeCount; ++x) LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3); - printf("Done! (%zu LiqTypes loaded)\n", liqTypeCount); + printf("Done! (%u LiqTypes loaded)\n", (uint32)liqTypeCount); } // @@ -277,7 +277,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"; @@ -293,6 +293,8 @@ struct map_fileheader uint32 heightMapSize; uint32 liquidMapOffset; uint32 liquidMapSize; + uint32 holesOffset; + uint32 holesSize; }; #define MAP_AREA_NO_AREA 0x0001 @@ -827,9 +829,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; @@ -876,6 +907,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/map_extractor/adt.cpp b/src/tools/map_extractor/adt.cpp index fde70681113..30524dbd640 100644 --- a/src/tools/map_extractor/adt.cpp +++ b/src/tools/map_extractor/adt.cpp @@ -6,6 +6,13 @@ int holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888}; int holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000}; +u_map_fcc MHDRMagic = { {'R','D','H','M'} }; +u_map_fcc MCINMagic = { {'N','I','C','M'} }; +u_map_fcc MH2OMagic = { {'O','2','H','M'} }; +u_map_fcc MCNKMagic = { {'K','N','C','M'} }; +u_map_fcc MCVTMagic = { {'T','V','C','M'} }; +u_map_fcc MCLQMagic = { {'Q','L','C','M'} }; + bool isHole(int holes, int i, int j) { int testi = i / 2; @@ -53,7 +60,7 @@ bool ADT_file::prepareLoadedData() bool adt_MHDR::prepareLoadedData() { - if (fcc != 'MHDR') + if (fcc != MHDRMagic.fcc) return false; if (size!=sizeof(adt_MHDR)-8) @@ -72,7 +79,7 @@ bool adt_MHDR::prepareLoadedData() bool adt_MCIN::prepareLoadedData() { - if (fcc != 'MCIN') + if (fcc != MCINMagic.fcc) return false; // Check cells data @@ -86,7 +93,7 @@ bool adt_MCIN::prepareLoadedData() bool adt_MH2O::prepareLoadedData() { - if (fcc != 'MH2O') + if (fcc != MH2OMagic.fcc) return false; // Check liquid data @@ -98,7 +105,7 @@ bool adt_MH2O::prepareLoadedData() bool adt_MCNK::prepareLoadedData() { - if (fcc != 'MCNK') + if (fcc != MCNKMagic.fcc) return false; // Check height map @@ -113,7 +120,7 @@ bool adt_MCNK::prepareLoadedData() bool adt_MCVT::prepareLoadedData() { - if (fcc != 'MCVT') + if (fcc != MCVTMagic.fcc) return false; if (size != sizeof(adt_MCVT)-8) @@ -124,8 +131,8 @@ bool adt_MCVT::prepareLoadedData() bool adt_MCLQ::prepareLoadedData() { - if (fcc != 'MCLQ') + if (fcc != MCLQMagic.fcc) return false; return true; -}
\ No newline at end of file +} diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp index 465eb04083f..5dcb479a11c 100644 --- a/src/tools/map_extractor/loadlib.cpp +++ b/src/tools/map_extractor/loadlib.cpp @@ -6,6 +6,8 @@ class MPQFile; +u_map_fcc MverMagic = { {'R','E','V','M'} }; + FileLoader::FileLoader() { data = 0; @@ -49,7 +51,7 @@ bool FileLoader::prepareLoadedData() { // Check version version = (file_MVER *) data; - if (version->fcc != 'MVER') + if (version->fcc != MverMagic.fcc) return false; if (version->ver != FILE_FORMAT_VERSION) return false; diff --git a/src/tools/map_extractor/loadlib/loadlib.h b/src/tools/map_extractor/loadlib/loadlib.h index bf6c0706d46..f32f46c63b9 100644 --- a/src/tools/map_extractor/loadlib/loadlib.h +++ b/src/tools/map_extractor/loadlib/loadlib.h @@ -29,6 +29,12 @@ typedef uint8_t uint8; #define FILE_FORMAT_VERSION 18 +union u_map_fcc +{ + char fcc_txt[4]; + uint32 fcc; +}; + // // File version chunk // diff --git a/src/tools/map_extractor/mpq_libmpq.cpp b/src/tools/map_extractor/mpq_libmpq.cpp index 81aa8cc2894..1c1a12e7b85 100644 --- a/src/tools/map_extractor/mpq_libmpq.cpp +++ b/src/tools/map_extractor/mpq_libmpq.cpp @@ -79,7 +79,7 @@ size_t MPQFile::read(void* dest, size_t bytes) if (eof) return 0; size_t rpos = pointer + bytes; - if (rpos > size) { + if (rpos > size_t(size)) { bytes = size - pointer; eof = true; } diff --git a/src/tools/map_extractor/mpq_libmpq04.h b/src/tools/map_extractor/mpq_libmpq04.h index 89f715e9e87..9f0163067c4 100644 --- a/src/tools/map_extractor/mpq_libmpq04.h +++ b/src/tools/map_extractor/mpq_libmpq04.h @@ -1,6 +1,3 @@ -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_WARNINGS - #ifndef MPQ_H #define MPQ_H diff --git a/src/tools/map_extractor/wdt.cpp b/src/tools/map_extractor/wdt.cpp index dedefbb64e5..ff255145583 100644 --- a/src/tools/map_extractor/wdt.cpp +++ b/src/tools/map_extractor/wdt.cpp @@ -2,23 +2,27 @@ #include "wdt.h" +u_map_fcc MWMOMagic = { {'O', 'M', 'W', 'M'} }; +u_map_fcc MPHDMagic = { {'D', 'H', 'P', 'M'} }; +u_map_fcc MAINMagic = { {'N', 'I', 'A', 'M'} }; + bool wdt_MWMO::prepareLoadedData() { - if (fcc != 'MWMO') + if (fcc != MWMOMagic.fcc) return false; return true; } bool wdt_MPHD::prepareLoadedData() { - if (fcc != 'MPHD') + if (fcc != MPHDMagic.fcc) return false; return true; } bool wdt_MAIN::prepareLoadedData() { - if (fcc != 'MAIN') + if (fcc != MAINMagic.fcc) return false; return true; } @@ -59,4 +63,4 @@ bool WDT_file::prepareLoadedData() if (!wmo->prepareLoadedData()) return false; return true; -}
\ No newline at end of file +} diff --git a/src/tools/mesh_extractor/ADT.cpp b/src/tools/mesh_extractor/ADT.cpp new file mode 100644 index 00000000000..c2ac19d5be0 --- /dev/null +++ b/src/tools/mesh_extractor/ADT.cpp @@ -0,0 +1,53 @@ +#include "ADT.h" +#include "DoodadHandler.h" +#include "LiquidHandler.h" +#include "WorldModelHandler.h" + +ADT::ADT( std::string file ) : ObjectData(NULL), Data(NULL), HasObjectData(false), + _DoodadHandler(NULL), _WorldModelHandler(NULL), _LiquidHandler(NULL) +{ + Data = new ChunkedData(file); + ObjectData = new ChunkedData(Utils::Replace(file, ".adt", "_obj0.adt")); + if (ObjectData->Stream) + HasObjectData = true; + else + ObjectData = NULL; +} + +ADT::~ADT() +{ + delete ObjectData; + delete Data; + + for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr) + delete *itr; + + MapChunks.clear(); + delete _DoodadHandler; + delete _WorldModelHandler; + delete _LiquidHandler; +} + +void ADT::Read() +{ + Header.Read(Data->GetChunkByName("MHDR")->GetStream()); + MapChunks.reserve(16 * 16); + + for (std::vector<Chunk*>::iterator itr = Data->Chunks.begin(); itr != Data->Chunks.end(); ++itr) + if ((*itr)->Name == "MCNK") + MapChunks.push_back(new MapChunk(this, *itr)); + + _LiquidHandler = new LiquidHandler(this); + + // do this separate from map chunk initialization to access liquid data + for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr) + (*itr)->GenerateTriangles(); + + _DoodadHandler = new DoodadHandler(this); + for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr) + _DoodadHandler->ProcessMapChunk(*itr); + + _WorldModelHandler = new WorldModelHandler(this); + for (std::vector<MapChunk*>::iterator itr = MapChunks.begin(); itr != MapChunks.end(); ++itr) + _WorldModelHandler->ProcessMapChunk(*itr); +} diff --git a/src/tools/mesh_extractor/ADT.h b/src/tools/mesh_extractor/ADT.h new file mode 100644 index 00000000000..133596eb024 --- /dev/null +++ b/src/tools/mesh_extractor/ADT.h @@ -0,0 +1,29 @@ +#ifndef ADT_H +#define ADT_H +#include "ChunkedData.h" +#include "MapChunk.h" + +class DoodadHandler; +class WorldModelHandler; +class LiquidHandler; + +class ADT +{ +public: + ADT(std::string file); + ~ADT(); + + void Read(); + + ChunkedData* ObjectData; + ChunkedData* Data; + std::vector<MapChunk*> MapChunks; + MHDR Header; + // Can we dispose of this? + bool HasObjectData; + + DoodadHandler* _DoodadHandler; + WorldModelHandler* _WorldModelHandler; + LiquidHandler* _LiquidHandler; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/CMakeLists.txt b/src/tools/mesh_extractor/CMakeLists.txt new file mode 100644 index 00000000000..9ed8472051d --- /dev/null +++ b/src/tools/mesh_extractor/CMakeLists.txt @@ -0,0 +1,50 @@ +# Copyright (C) 2005-2009 MaNGOS project <http://getmangos.com/> +# Copyright (C) 2008-2013 TrinityCore <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 meshExtract_Sources *.cpp *.h) + +set(include_Base + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src/server/shared + ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Recast + ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour + ${CMAKE_SOURCE_DIR}/dep/libmpq + ${CMAKE_SOURCE_DIR}/dep/g3dlite/include + ${ACE_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if( WIN32 ) + set(include_Base + ${include_Base} + ${CMAKE_SOURCE_DIR}/dep/libmpq/win + ) +endif() + +include_directories(${include_Base}) + +add_executable(MeshExtractor ${meshExtract_Sources}) + +target_link_libraries(MeshExtractor + g3dlib + mpq + Recast + Detour + ${BZIP2_LIBRARIES} + ${ZLIB_LIBRARIES} + ${ACE_LIBRARY} +) + +if( UNIX ) + install(TARGETS MeshExtractor DESTINATION bin) +elseif( WIN32 ) + install(TARGETS MeshExtractor DESTINATION "${CMAKE_INSTALL_PREFIX}") +endif() diff --git a/src/tools/mesh_extractor/Cache.h b/src/tools/mesh_extractor/Cache.h new file mode 100644 index 00000000000..60e3d8434cf --- /dev/null +++ b/src/tools/mesh_extractor/Cache.h @@ -0,0 +1,63 @@ +#ifndef CACHE_H +#define CACHE_H +#include <string> +#include <map> +#include "Define.h" +#include <ace/Guard_T.h> +#include <ace/Synch.h> + +class WorldModelRoot; +class Model; + +template<class K, class T> +class GenericCache +{ +public: + GenericCache() {} + + static const uint32 FlushLimit = 1000; + + void Insert(K key, T* val) + { + ACE_GUARD(ACE_Thread_Mutex, g, mutex); + + if (_items.size() > FlushLimit) + Clear(); + _items[key] = val; + } + + T* Get(K key) + { + ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); + typename std::map<K, T*>::iterator itr = _items.find(key); + if (itr != _items.end()) + return itr->second; + return NULL; + } + + void Clear() + { + for (typename std::map<K, T*>::iterator itr = _items.begin(); itr != _items.end(); ++itr) + delete itr->second; + _items.clear(); + } +private: + std::map<K, T*> _items; + ACE_Thread_Mutex mutex; +}; + +class CacheClass +{ +public: + CacheClass() {} + GenericCache<std::string, Model> ModelCache; + GenericCache<std::string, WorldModelRoot> WorldModelCache; + + void Clear() + { + + } +}; + +extern CacheClass* Cache; +#endif diff --git a/src/tools/mesh_extractor/Chunk.cpp b/src/tools/mesh_extractor/Chunk.cpp new file mode 100644 index 00000000000..4605ae0f0dd --- /dev/null +++ b/src/tools/mesh_extractor/Chunk.cpp @@ -0,0 +1,31 @@ +#include "Chunk.h" +#include "Utils.h" + +int32 Chunk::FindSubChunkOffset(std::string name) +{ + // Reverse the name + name = std::string(name.rbegin(), name.rend()); + if (name.size() != 4) + return -1; + + FILE* stream = GetStream(); + uint32 matched = 0; + while (uint32(ftell(stream)) < Utils::Size(stream)) + { + char b = 0; + if (fread(&b, sizeof(char), 1, stream) != 1 || b != name[matched]) + matched = 0; + else + ++matched; + + if (matched == 4) + return ftell(stream) - 4; + } + return -1; +} + +FILE* Chunk::GetStream() +{ + fseek(Stream, Offset, SEEK_SET); + return Stream; +} diff --git a/src/tools/mesh_extractor/Chunk.h b/src/tools/mesh_extractor/Chunk.h new file mode 100644 index 00000000000..f3681a9f576 --- /dev/null +++ b/src/tools/mesh_extractor/Chunk.h @@ -0,0 +1,20 @@ +#ifndef CHUNK_H +#define CHUNK_H +#include "Define.h" +#include <string> +class ChunkedData; + +class Chunk +{ +public: + Chunk(const char* name, uint32 length, uint32 offset, FILE* stream) : Name(name), Length(length), Offset(offset), Stream(stream) {} + + int32 FindSubChunkOffset(std::string name); + FILE* GetStream(); + std::string Name; + uint32 Length; + uint32 Offset; + FILE* Stream; +}; + +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/ChunkedData.cpp b/src/tools/mesh_extractor/ChunkedData.cpp new file mode 100644 index 00000000000..e0db12a6be7 --- /dev/null +++ b/src/tools/mesh_extractor/ChunkedData.cpp @@ -0,0 +1,74 @@ +#include "ChunkedData.h" +#include "MPQManager.h" +#include "Utils.h" + +#include <string> + +ChunkedData::ChunkedData( FILE* stream, uint32 maxLength, uint32 chunksHint /*= 300*/ ) : +Stream(stream) +{ + if (!Stream) + return; + Load(maxLength, chunksHint); +} + +ChunkedData::ChunkedData( std::string file, uint32 chunksHint /*= 300*/ ) +{ + Stream = MPQHandler->GetFile(file); + if (!Stream) + return; + Load(0, chunksHint); +} + +void ChunkedData::Load( uint32 maxLength, uint32 chunksHint ) +{ + if (!maxLength) + maxLength = Utils::Size(Stream); + Chunks.reserve(chunksHint); + uint32 baseOffset = ftell(Stream); + uint32 calcOffset = 0; + while ((calcOffset + baseOffset) < Utils::Size(Stream) && (calcOffset < maxLength)) + { + char nameBytes[5]; + uint32 read = fread(&nameBytes, sizeof(char), 4, Stream); + nameBytes[read] = '\0'; + std::string name = std::string(nameBytes); + // Utils::Reverse(nameBytes); + name = std::string(name.rbegin(), name.rend()); + uint32 length; + if (fread(&length, sizeof(uint32), 1, Stream) != 1) + continue; + calcOffset += 8; + Chunks.push_back(new Chunk(name.c_str(), length, calcOffset + baseOffset, Stream)); + calcOffset += length; + // save an extra seek at the end + if ((calcOffset + baseOffset) < Utils::Size(Stream) && calcOffset < maxLength) + fseek(Stream, length, SEEK_CUR); + } +} + +int ChunkedData::GetFirstIndex( std::string name ) +{ + for (uint32 i = 0; i < Chunks.size(); ++i) + if (Chunks[i]->Name == name) + return i; + return -1; +} + +Chunk* ChunkedData::GetChunkByName( std::string name ) +{ + for (uint32 i = 0; i < Chunks.size(); ++i) + if (Chunks[i]->Name == name) + return Chunks[i]; + return NULL; +} + +ChunkedData::~ChunkedData() +{ + for (std::vector<Chunk*>::iterator itr = Chunks.begin(); itr != Chunks.end(); ++itr) + delete *itr; + + Chunks.clear(); + if (Stream) + fclose(Stream); +} diff --git a/src/tools/mesh_extractor/ChunkedData.h b/src/tools/mesh_extractor/ChunkedData.h new file mode 100644 index 00000000000..e23648c845e --- /dev/null +++ b/src/tools/mesh_extractor/ChunkedData.h @@ -0,0 +1,21 @@ +#ifndef CHNK_H +#define CHNK_H + +#include <vector> +#include "Chunk.h" + +class ChunkedData +{ +public: + ChunkedData(FILE* stream, uint32 maxLength, uint32 chunksHint = 300); + ChunkedData(std::string file, uint32 chunksHint = 300); + ~ChunkedData(); + + int GetFirstIndex(std::string name); + Chunk* GetChunkByName(std::string name); + + void Load(uint32 maxLength, uint32 chunksHint); + std::vector<Chunk*> Chunks; + FILE* Stream; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/Constants.h b/src/tools/mesh_extractor/Constants.h new file mode 100644 index 00000000000..02e2d25559f --- /dev/null +++ b/src/tools/mesh_extractor/Constants.h @@ -0,0 +1,57 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +class Constants +{ +public: + enum TriangleType + { + TRIANGLE_TYPE_UNKNOWN, + TRIANGLE_TYPE_TERRAIN, + TRIANGLE_TYPE_WATER, + TRIANGLE_TYPE_DOODAD, + TRIANGLE_TYPE_WMO + }; + + enum PolyArea + { + POLY_AREA_TERRAIN = 1, + POLY_AREA_WATER = 2, + POLY_AREA_ROAD = 3, + POLY_AREA_DANGER = 4, + }; + + enum PolyFlag + { + POLY_FLAG_WALK = 1, + POLY_FLAG_SWIM = 2, + POLY_FLAG_FLIGHTMASTER = 4 + }; + + enum ExtractFlags + { + EXTRACT_FLAG_DBC = 0x01, + EXTRACT_FLAG_MAPS = 0x02, + EXTRACT_FLAG_VMAPS = 0x04, + EXTRACT_FLAG_GOB_MODELS = 0x08, + EXTRACT_FLAG_MMAPS = 0x10, + EXTRACT_FLAG_TEST = 0x20, + EXTRACT_FLAG_ALLOWED = EXTRACT_FLAG_DBC | EXTRACT_FLAG_MAPS | EXTRACT_FLAG_VMAPS | EXTRACT_FLAG_GOB_MODELS | EXTRACT_FLAG_MMAPS | EXTRACT_FLAG_TEST + }; + + static const float TileSize; + static const float MaxXY; + static const float ChunkSize; + static const float UnitSize; + static const float Origin[]; + static const float PI; + static const float MaxStandableHeight; + static bool ToWoWCoords; + static const char* VMAPMagic; + static const float BaseUnitDim; + static const int VertexPerMap; + static const int VertexPerTile; + static const int TilesPerMap; +}; + +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/ContinentBuilder.cpp b/src/tools/mesh_extractor/ContinentBuilder.cpp new file mode 100644 index 00000000000..d6125bdd8e2 --- /dev/null +++ b/src/tools/mesh_extractor/ContinentBuilder.cpp @@ -0,0 +1,144 @@ +#include "ContinentBuilder.h" +#include "TileBuilder.h" +#include "WDT.h" +#include "Utils.h" +#include "DetourNavMesh.h" +#include "Cache.h" +#include "ace/Task.h" +#include "Recast.h" + +class BuilderThread : public ACE_Task_Base +{ +private: + int X, Y, MapId; + std::string Continent; + bool debug; + dtNavMeshParams Params; + ContinentBuilder* cBuilder; +public: + BuilderThread(ContinentBuilder* _cBuilder, bool deb, dtNavMeshParams& params) : debug(deb), Params(params), cBuilder(_cBuilder), Free(true) {} + void SetData(int x, int y, int map, std::string cont) { X = x; Y = y; MapId = map; Continent = cont; } + + int svc() + { + Free = false; + printf("[%02i,%02i] Building tile\n", X, Y); + TileBuilder builder(cBuilder, Continent, X, Y, MapId); + char buff[100]; + sprintf(buff, "mmaps/%03u%02u%02u.mmtile", MapId, Y, X); + FILE* f = fopen(buff, "r"); + if (f) // Check if file already exists. + { + printf("[%02i,%02i] Tile skipped, file already exists\n", X, Y); + fclose(f); + Free = true; + return 0; + } + uint8* nav = builder.Build(debug, Params); + if (nav) + { + f = fopen(buff, "wb"); + if (!f) + { + printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff); + return 0; + } + MmapTileHeader header; + header.size = builder.DataSize; + fwrite(&header, sizeof(MmapTileHeader), 1, f); + fwrite(nav, sizeof(unsigned char), builder.DataSize, f); + fclose(f); + } + dtFree(nav); + printf("[%02u,%02u] Tile Built!\n", X, Y); + Free = true; + return 0; + } + + bool Free; +}; + +void ContinentBuilder::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)) * Constants::TileSize; + bmax[2] = (32 - int(tileY)) * Constants::TileSize; + bmin[0] = bmax[0] - Constants::TileSize; + bmin[2] = bmax[2] - Constants::TileSize; +} + +void ContinentBuilder::CalculateTileBounds() +{ + for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr) + { + tileXMax = std::max(itr->X, tileXMax); + tileXMin = std::min(itr->X, tileXMin); + + tileYMax = std::max(itr->Y, tileYMax); + tileYMin = std::min(itr->Y, tileYMin); + } + getTileBounds(tileXMax, tileYMax, NULL, 0, bmin, bmax); +} + +void ContinentBuilder::Build(bool debug) +{ + char buff[50]; + sprintf(buff, "mmaps/%03u.mmap", MapId); + FILE* mmap = fopen(buff, "wb"); + if (!mmap) + { + printf("Could not create file %s. Check that you have write permissions to the destination folder and try again\n", buff); + return; + } + + CalculateTileBounds(); + + dtNavMeshParams params; + params.maxPolys = 1 << STATIC_POLY_BITS; + params.maxTiles = TileMap->TileTable.size(); + rcVcopy(params.orig, bmin); + params.tileHeight = Constants::TileSize; + params.tileWidth = Constants::TileSize; + fwrite(¶ms, sizeof(dtNavMeshParams), 1, mmap); + fclose(mmap); + std::vector<BuilderThread*> Threads; + for (uint32 i = 0; i < NumberOfThreads; ++i) + Threads.push_back(new BuilderThread(this, debug, params)); + printf("Map %s ( %i ) has %u tiles. Building them with %i threads\n", Continent.c_str(), MapId, uint32(TileMap->TileTable.size()), NumberOfThreads); + for (std::vector<TilePos>::iterator itr = TileMap->TileTable.begin(); itr != TileMap->TileTable.end(); ++itr) + { + bool next = false; + while (!next) + { + for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th) + { + if ((*_th)->Free) + { + (*_th)->SetData(itr->X, itr->Y, MapId, Continent); + (*_th)->activate(); + next = true; + break; + } + } + // Wait for 20 seconds + ACE_OS::sleep(ACE_Time_Value (0, 20000)); + } + } + Cache->Clear(); + + // Free memory + for (std::vector<BuilderThread*>::iterator _th = Threads.begin(); _th != Threads.end(); ++_th) + { + (*_th)->wait(); + delete *_th; + } +} diff --git a/src/tools/mesh_extractor/ContinentBuilder.h b/src/tools/mesh_extractor/ContinentBuilder.h new file mode 100644 index 00000000000..b36ca125b9e --- /dev/null +++ b/src/tools/mesh_extractor/ContinentBuilder.h @@ -0,0 +1,30 @@ +#ifndef CONT_BUILDER_H +#define CONT_BUILDER_H +#include <string> +#include "WDT.h" +#include "Define.h" + +class ContinentBuilder +{ +public: + ContinentBuilder(std::string continent, uint32 mapId, WDT* wdt, uint32 tn) : + Continent(continent), TileMap(wdt), MapId(mapId), + NumberOfThreads(tn), tileXMin(64), tileYMin(64), tileXMax(0), tileYMax(0) + {} + + void Build(bool debug); + void getTileBounds(uint32 tileX, uint32 tileY, float* verts, int vertCount, float* bmin, float* bmax); + void CalculateTileBounds(); + float bmin[3]; + float bmax[3]; +private: + std::string Continent; + WDT* TileMap; + uint32 MapId; + uint32 NumberOfThreads; + int tileXMin; + int tileYMin; + int tileXMax; + int tileYMax; +}; +#endif diff --git a/src/tools/mesh_extractor/DBC.cpp b/src/tools/mesh_extractor/DBC.cpp new file mode 100644 index 00000000000..9a55ff6d7ed --- /dev/null +++ b/src/tools/mesh_extractor/DBC.cpp @@ -0,0 +1,70 @@ +#include <cstdio> +#include "DBC.h" +#include "Define.h" + +DBC::DBC( FILE* stream ) : StringBlock(NULL), StringBlockSize(0), IsFaulty(true) +{ + char magic[5]; + uint32 count = 0; + count += fread(&magic, sizeof(char), 4, stream); + magic[4] = '\0'; + count += fread(&RecordCount, sizeof(uint32), 1, stream); + Records.reserve(RecordCount); + count += fread(&Fields, sizeof(uint32), 1, stream); + count += fread(&RecordSize, sizeof(uint32), 1, stream); + count += fread(&StringBlockSize, sizeof(uint32), 1, stream); + if (count != 8) + printf("DBC::DBC: Failed to read some data expected 8, read %u\n", count); + + for (int i = 0; i < RecordCount; i++) + { + Record* rec = new Record(this); + Records.push_back(rec); + int size = 0; + for (int f = 0; f < Fields; f++) + { + if (size + 4 > RecordSize) + { + IsFaulty = true; + break; + } + uint32 tmp; + if (fread(&tmp, sizeof(uint32), 1, stream) != 1) + printf("DBC::DBC: Failed to read some data expected 1, read 0\n"); + rec->Values.push_back(tmp); + size += 4; + } + } + StringBlock = new uint8[StringBlockSize]; + count = fread(StringBlock, sizeof(uint8), StringBlockSize, stream); + if (count != StringBlockSize) + printf("DBC::DBC: Failed to read some data expected %u, read %u\n", StringBlockSize, count); +} + +std::string DBC::GetStringByOffset( int offset ) +{ + int len = 0; + for (uint32 i = offset; i < StringBlockSize; i++) + { + if (!StringBlock[i]) + { + len = (i - offset); + break; + } + } + char* d = new char[len+1]; + strcpy(d, (const char*)(StringBlock + offset)); + d[len] = '\0'; + std::string val = std::string(d); + delete d; + return val; +} + +Record* DBC::GetRecordById( int id ) +{ + // we assume Id is index 0 + for (std::vector<Record*>::iterator itr = Records.begin(); itr != Records.end(); ++itr) + if ((*itr)->Values[0] == id) + return *itr; + return NULL; +} diff --git a/src/tools/mesh_extractor/DBC.h b/src/tools/mesh_extractor/DBC.h new file mode 100644 index 00000000000..5ed57754e73 --- /dev/null +++ b/src/tools/mesh_extractor/DBC.h @@ -0,0 +1,53 @@ +#ifndef DBC_H +#define DBC_H +#include <vector> +#include <string> +#include "Define.h" + +class Record; + +class DBC +{ +public: + DBC(FILE* stream); + + std::string GetStringByOffset(int offset); + + Record* GetRecordById(int id); + + std::string Name; + std::vector<Record*> Records; + int RecordCount; + int Fields; + int RecordSize; + uint8* StringBlock; + uint32 StringBlockSize; + bool IsFaulty; +}; + +class Record +{ +public: + Record(DBC* dbc) : Source(dbc) {} + + DBC* Source; + std::vector<int> Values; + + int operator[](int index) + { + return Values[index]; + } + + template <typename T> + T GetValue(int index) + { + return *(T*)(&Values[index]); + } + + std::string GetString(int index) + { + return Source->GetStringByOffset(Values[index]); + } +}; + +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/DoodadHandler.cpp b/src/tools/mesh_extractor/DoodadHandler.cpp new file mode 100644 index 00000000000..56c2a7986f8 --- /dev/null +++ b/src/tools/mesh_extractor/DoodadHandler.cpp @@ -0,0 +1,109 @@ +#include "DoodadHandler.h" +#include "Chunk.h" +#include "Cache.h" +#include "Model.h" +#include "G3D/Matrix4.h" + +DoodadHandler::DoodadHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL) +{ + if (!adt->HasObjectData) + return; + Chunk* mddf = adt->ObjectData->GetChunkByName("MDDF"); + if (mddf) + ReadDoodadDefinitions(mddf); + + Chunk* mmid = adt->ObjectData->GetChunkByName("MMID"); + Chunk* mmdx = adt->ObjectData->GetChunkByName("MMDX"); + if (mmid && mmdx) + ReadDoodadPaths(mmid, mmdx); +} + +void DoodadHandler::ProcessInternal( ChunkedData* subChunks ) +{ + if (!IsSane()) + return; + Chunk* doodadReferencesChunk = subChunks->GetChunkByName("MCRD"); + if (!doodadReferencesChunk) + return; + FILE* stream = doodadReferencesChunk->GetStream(); + uint32 refCount = doodadReferencesChunk->Length / 4; + for (uint32 i = 0; i < refCount; i++) + { + int32 index; + if (int count = fread(&index, sizeof(int32), 1, stream) != 1) + printf("DoodadHandler::ProcessInternal: Failed to read some data expected 1, read %d\n", count); + if (index < 0 || uint32(index) >= _definitions->size()) + continue; + DoodadDefinition doodad = (*_definitions)[index]; + if (_drawn.find(doodad.UniqueId) != _drawn.end()) + continue; + _drawn.insert(doodad.UniqueId); + if (doodad.MmidIndex >= _paths->size()) + continue; + + std::string path = (*_paths)[doodad.MmidIndex]; + Model* model = Cache->ModelCache.Get(path); + if (!model) + { + model = new Model(path); + Cache->ModelCache.Insert(path, model); + } + if (!model->IsCollidable) + continue; + + Vertices.reserve(refCount * model->Vertices.size() * 0.2); + Triangles.reserve(refCount * model->Triangles.size() * 0.2); + + InsertModelGeometry(doodad, model); + } +} + +void DoodadHandler::ReadDoodadDefinitions( Chunk* chunk ) +{ + int32 count = chunk->Length / 36; + _definitions = new std::vector<DoodadDefinition>; + _definitions->reserve(count); + FILE* stream = chunk->GetStream(); + for (int i = 0; i < count; i++) + { + DoodadDefinition def; + def.Read(stream); + _definitions->push_back(def); + } +} + +void DoodadHandler::ReadDoodadPaths( Chunk* id, Chunk* data ) +{ + int paths = id->Length / 4; + _paths = new std::vector<std::string>(); + _paths->reserve(paths); + for (int i = 0; i < paths; i++) + { + FILE* idStream = id->GetStream(); + fseek(idStream, i * 4, SEEK_CUR); + uint32 offset; + if (fread(&offset, sizeof(uint32), 1, idStream) != 1) + printf("DoodadHandler::ReadDoodadPaths: Failed to read some data expected 1, read 0\n"); + FILE* dataStream = data->GetStream(); + fseek(dataStream, offset + data->Offset, SEEK_SET); + _paths->push_back(Utils::ReadString(dataStream)); + } +} + +void DoodadHandler::InsertModelGeometry(const DoodadDefinition& def, Model* model) +{ + G3D::Matrix4 transformation = Utils::GetTransformation(def); + uint32 vertOffset = Vertices.size(); + + for (std::vector<Vector3>::iterator itr = model->Vertices.begin(); itr != model->Vertices.end(); ++itr) + Vertices.push_back(Utils::VectorTransform(*itr, transformation)); + + for (std::vector<Triangle<uint16> >::iterator itr = model->Triangles.begin(); itr != model->Triangles.end(); ++itr) + Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_DOODAD, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset)); +} + +DoodadHandler::~DoodadHandler() +{ + delete _definitions; + delete _paths; +} diff --git a/src/tools/mesh_extractor/DoodadHandler.h b/src/tools/mesh_extractor/DoodadHandler.h new file mode 100644 index 00000000000..96aecbcce27 --- /dev/null +++ b/src/tools/mesh_extractor/DoodadHandler.h @@ -0,0 +1,57 @@ +#ifndef DOOADHNDL_H +#define DOOADHNDL_H +#include "ObjectDataHandler.h" +#include "Utils.h" +#include "Chunk.h" +#include "Model.h" +#include <set> +#include <vector> + +class DoodadDefinition : public IDefinition +{ +public: + uint32 MmidIndex; + uint32 UniqueId; + uint16 DecimalScale; + uint16 Flags; + + virtual float Scale() const { return DecimalScale / 1024.0f; } + + void Read(FILE* stream) + { + int count = 0; + + count += fread(&MmidIndex, sizeof(uint32), 1, stream); + count += fread(&UniqueId, sizeof(uint32), 1, stream); + Position = Vector3::Read(stream); + Rotation = Vector3::Read(stream); + count += fread(&DecimalScale, sizeof(uint16), 1, stream); + count += fread(&Flags, sizeof(uint16), 1, stream); + if (count != 4) + printf("DoodadDefinition::Read: Failed to read some data expected 4, read %d\n", count); + } +}; + +class DoodadHandler : public ObjectDataHandler +{ +public: + DoodadHandler(ADT* adt); + ~DoodadHandler(); + + std::vector<Vector3> Vertices; + std::vector<Triangle<uint32> > Triangles; + bool IsSane() { return _definitions && _paths; } + + +protected: + void ProcessInternal(ChunkedData* chunk); + +private: + void ReadDoodadDefinitions(Chunk* chunk); + void ReadDoodadPaths(Chunk* id, Chunk* data); + void InsertModelGeometry(const DoodadDefinition& def, Model* model); + std::set<uint32> _drawn; + std::vector<DoodadDefinition>* _definitions; + std::vector<std::string>* _paths; +}; +#endif diff --git a/src/tools/mesh_extractor/Geometry.cpp b/src/tools/mesh_extractor/Geometry.cpp new file mode 100644 index 00000000000..2fc470e8e9f --- /dev/null +++ b/src/tools/mesh_extractor/Geometry.cpp @@ -0,0 +1,123 @@ +#include "Geometry.h" +#include "Constants.h" +#include "ADT.h" +#include "WorldModelHandler.h" +#include "DoodadHandler.h" + +Geometry::Geometry() : Transform(false) +{ + Vertices.reserve(10000); + Triangles.reserve(10000); +} + +void Geometry::CalculateBoundingBox( float*& min, float*& max ) +{ + min = new float[3]; + max = new float[3]; + + for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr) + { + if (itr->x > max[0]) + max[0] = itr->x; + if (itr->x < min[0]) + min[0] = itr->x; + + if (itr->y > max[1]) + max[1] = itr->y; + if (itr->y < min[1]) + min[1] = itr->y; + + if (itr->z > max[2]) + max[2] = itr->z; + if (itr->z < min[2]) + min[2] = itr->z; + } +} + +void Geometry::CalculateMinMaxHeight( float& min, float& max ) +{ + min = 0.0f; + max = 0.0f; + + for (std::vector<Vector3>::iterator itr = Vertices.begin(); itr != Vertices.end(); ++itr) + { + if (Transform) + { + if (itr->y < min) + min = itr->y; + if (itr->y > max) + max = itr->y; + } + else + { + if (itr->z < min) + min = itr->z; + if (itr->z > max) + max = itr->z; + } + } +} + +void Geometry::AddData( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris ) +{ + uint32 vertOffset = Vertices.size(); + for (std::vector<Vector3>::iterator itr = verts.begin(); itr != verts.end(); ++itr) + Vertices.push_back(Transform ? Utils::ToRecast(*itr) : *itr); + + for (std::vector<Triangle<uint32> >::iterator itr = tris.begin(); itr != tris.end(); ++itr) + Triangles.push_back(Triangle<uint32>(itr->Type, itr->V0 + vertOffset, itr->V1 + vertOffset, itr->V2 + vertOffset)); +} + +void Geometry::GetRawData( float*& verts, int*& tris, uint8*& areas ) +{ + verts = new float[Vertices.size() * 3]; + for (uint32 i = 0; i < Vertices.size(); ++i) + { + Vector3& vert = Vertices[i]; + verts[(i * 3) + 0] = vert.x; + verts[(i * 3) + 1] = vert.y; + verts[(i * 3) + 2] = vert.z; + } + + tris = new int[Triangles.size() * 3]; + for (uint32 i = 0; i < Triangles.size(); ++i) + { + Triangle<uint32>& tri = Triangles[i]; + tris[(i * 3) + 0] = (int)tri.V0; + tris[(i * 3) + 1] = (int)tri.V1; + tris[(i * 3) + 2] = (int)tri.V2; + } + + areas = new uint8[Triangles.size()]; + for (uint32 i = 0; i < Triangles.size(); i++) + { + switch (Triangles[i].Type) + { + case Constants::TRIANGLE_TYPE_WATER: + areas[i] = Constants::POLY_AREA_WATER; + break; + default: + areas[i] = Constants::POLY_AREA_TERRAIN; + break; + } + } +} + +void Geometry::AddAdt( ADT* adt ) +{ + for (std::vector<MapChunk*>::iterator itr = adt->MapChunks.begin(); itr != adt->MapChunks.end(); ++itr) + { + std::vector<Triangle<uint32> > tmp; + tmp.reserve((*itr)->Triangles.size()); + for (std::vector<Triangle<uint8> >::iterator itr2 = (*itr)->Triangles.begin(); itr2 != (*itr)->Triangles.end(); ++itr2) + tmp.push_back(Triangle<uint32>(itr2->Type, itr2->V0, itr2->V1, itr2->V2)); + AddData((*itr)->Vertices, tmp); + } + + if (!adt->_DoodadHandler->Triangles.empty()) + AddData(adt->_DoodadHandler->Vertices, adt->_DoodadHandler->Triangles); + + if (!adt->_WorldModelHandler->Triangles.empty()) + AddData(adt->_WorldModelHandler->Vertices, adt->_WorldModelHandler->Triangles); +} + diff --git a/src/tools/mesh_extractor/Geometry.h b/src/tools/mesh_extractor/Geometry.h new file mode 100644 index 00000000000..e445234dd12 --- /dev/null +++ b/src/tools/mesh_extractor/Geometry.h @@ -0,0 +1,23 @@ +#ifndef GEOMETRY_H +#define GEOMETRY_H +#include <vector> + +#include "Utils.h" + +class ADT; +class Geometry +{ +public: + Geometry(); + + void CalculateBoundingBox(float*& min, float*& max); + void CalculateMinMaxHeight(float& min, float& max); + void AddData(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris); + void AddAdt(ADT* adt); + void GetRawData(float*& verts, int*& tris, uint8*& areas); + + std::vector<Vector3> Vertices; + std::vector<Triangle<uint32> > Triangles; + bool Transform; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/LiquidHandler.cpp b/src/tools/mesh_extractor/LiquidHandler.cpp new file mode 100644 index 00000000000..285ea1a5b74 --- /dev/null +++ b/src/tools/mesh_extractor/LiquidHandler.cpp @@ -0,0 +1,102 @@ +#include "LiquidHandler.h" +#include "Utils.h" + +LiquidHandler::LiquidHandler( ADT* adt ) : Source(adt) +{ + HandleNewLiquid(); +} + +void LiquidHandler::HandleNewLiquid() +{ + Chunk* chunk = Source->Data->GetChunkByName("MH2O"); + if (!chunk) + return; + + Vertices.reserve(1000); + Triangles.reserve(1000); + + FILE* stream = chunk->GetStream(); + H2OHeader header[256]; + MCNKData.reserve(256); + for (int i = 0; i < 256; i++) + header[i] = H2OHeader::Read(stream); + + for (int i = 0; i < 256; i++) + { + H2OHeader h = header[i]; + if (h.LayerCount == 0) + { + // Need to fill in missing data with dummies. + MCNKData.push_back(MCNKLiquidData(NULL, H2ORenderMask())); + continue; + } + fseek(stream, chunk->Offset + h.OffsetInformation, SEEK_SET); + H2OInformation information = H2OInformation::Read(stream); + + float** heights = new float*[9]; + for (int j = 0; j < 9; ++i) + { + heights[j] = new float[9]; + memset(heights[j], 0, sizeof(float) * 9); + } + + H2ORenderMask renderMask; + if (information.LiquidType != 2) + { + fseek(stream, chunk->Offset + h.OffsetRender, SEEK_SET); + renderMask = H2ORenderMask::Read(stream); + if ((Utils::IsAllZero(renderMask.Mask, 8) || (information.Width == 8 && information.Height == 8)) && information.OffsetMask2) + { + fseek(stream, chunk->Offset + information.OffsetMask2, SEEK_SET); + uint32 size = ceil(information.Width * information.Height / 8.0f); + uint8* altMask = new uint8[size]; + if (fread(altMask, sizeof(uint8), size, stream) == size) + for (uint32 mi = 0; mi < size; mi++) + renderMask.Mask[mi + information.OffsetY] |= altMask[mi]; + delete[] altMask; + } + fseek(stream, chunk->Offset + information.OffsetHeightmap, SEEK_SET); + + for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++) + for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++) + if (fread(&heights[x][y], sizeof(float), 1, stream) != 1) + return; + } + else + { + // Fill with ocean data + for (uint32 i = 0; i < 8; ++i) + renderMask.Mask[i] = 0xFF; + + for (uint32 y = 0; y < 9; ++y) + for (uint32 x = 0; x < 9; ++x) + heights[x][y] = information.HeightLevel1; + } + + MCNKData.push_back(MCNKLiquidData(heights, renderMask)); + + for (int y = information.OffsetY; y < (information.OffsetY + information.Height); y++) + { + for (int x = information.OffsetX; x < (information.OffsetX + information.Width); x++) + { + if (!renderMask.ShouldRender(x, y)) + continue; + + MapChunk* mapChunk = Source->MapChunks[i]; + Vector3 location = mapChunk->Header.Position; + location.y = location.y - (x * Constants::UnitSize); + location.x = location.x - (y * Constants::UnitSize); + location.z = heights[x][y]; + + uint32 vertOffset = Vertices.size(); + Vertices.push_back(location); + Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y, location.z)); + Vertices.push_back(Vector3(location.x, location.y - Constants::UnitSize, location.z)); + Vertices.push_back(Vector3(location.x - Constants::UnitSize, location.y - Constants::UnitSize, location.z)); + + Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset+2, vertOffset + 1)); + Triangles.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1)); + } + } + } +} diff --git a/src/tools/mesh_extractor/LiquidHandler.h b/src/tools/mesh_extractor/LiquidHandler.h new file mode 100644 index 00000000000..41e128ba20b --- /dev/null +++ b/src/tools/mesh_extractor/LiquidHandler.h @@ -0,0 +1,21 @@ +#ifndef LIQUID_H +#define LIQUID_H +#include "ADT.h" +#include "Utils.h" +#include "Define.h" + +#include <vector> + +class LiquidHandler +{ +public: + LiquidHandler(ADT* adt); + + ADT* Source; + std::vector<Vector3> Vertices; + std::vector<Triangle<uint32> > Triangles; + std::vector<MCNKLiquidData> MCNKData; +private: + void HandleNewLiquid(); +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/MPQ.cpp b/src/tools/mesh_extractor/MPQ.cpp new file mode 100644 index 00000000000..18a9eb0f0e3 --- /dev/null +++ b/src/tools/mesh_extractor/MPQ.cpp @@ -0,0 +1,118 @@ +#include "MPQ.h" +#include "MPQManager.h" +#include <deque> +#include <cstdio> + +MPQArchive::MPQArchive(const char* filename) +{ + int result = libmpq__archive_open(&mpq_a, filename, -1); + printf("Opening %s\n", filename); + if (result) + { + switch (result) + { + case LIBMPQ_ERROR_OPEN : + printf("Error opening archive '%s': Does file really exist?\n", filename); + break; + case LIBMPQ_ERROR_FORMAT : /* bad file format */ + printf("Error opening archive '%s': Bad file format\n", filename); + break; + case LIBMPQ_ERROR_SEEK : /* seeking in file failed */ + printf("Error opening archive '%s': Seeking in file failed\n", filename); + break; + case LIBMPQ_ERROR_READ : /* Read error in archive */ + printf("Error opening archive '%s': Read error in archive\n", filename); + break; + case LIBMPQ_ERROR_MALLOC : /* maybe not enough memory? :) */ + printf("Error opening archive '%s': Maybe not enough memory\n", filename); + break; + default: + printf("Error opening archive '%s': Unknown error\n", filename); + break; + } + } + GetFileListTo(Files); +} + +void MPQArchive::close() +{ + libmpq__archive_close(mpq_a); +} + +MPQFile::MPQFile(const char* filename): +eof(false), buffer(0), pointer(0), size(0) +{ + for (std::deque<MPQArchive*>::iterator i = MPQHandler->Archives.begin(); i != MPQHandler->Archives.end();++i) + { + mpq_archive* mpq_a = (*i)->mpq_a; + + uint32_t filenum; + if(libmpq__file_number(mpq_a, filename, &filenum)) + continue; + libmpq__off_t transferred; + libmpq__file_unpacked_size(mpq_a, filenum, &size); + + // HACK: in patch.mpq some files don't want to open and give 1 for filesize + if (size<=1) { + // printf("warning: file %s has size %d; cannot Read.\n", filename, size); + eof = true; + buffer = 0; + return; + } + buffer = new char[size]; + + //libmpq_file_getdata + libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); + /*libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);*/ + return; + + } + eof = true; + buffer = 0; +} + +size_t MPQFile::Read(void* dest, size_t bytes) +{ + if (eof) + return 0; + + size_t rpos = pointer + bytes; + if (rpos > size_t(size)) { + bytes = size - pointer; + eof = true; + } + + memcpy(dest, &(buffer[pointer]), bytes); + + pointer = rpos; + + return bytes; +} + +void MPQFile::seek(int offset) +{ + pointer = offset; + eof = (pointer >= size); +} + +void MPQFile::seekRelative(int offset) +{ + pointer += offset; + eof = (pointer >= size); +} + +void MPQFile::close() +{ + if (buffer) + delete[] buffer; + buffer = 0; + eof = true; +} + +FILE* MPQFile::GetFileStream() +{ + FILE* file = tmpfile(); + fwrite(buffer, sizeof(char), size, file); + fseek(file, 0, SEEK_SET); + return file; +} diff --git a/src/tools/mesh_extractor/MPQ.h b/src/tools/mesh_extractor/MPQ.h new file mode 100644 index 00000000000..2f8b082f526 --- /dev/null +++ b/src/tools/mesh_extractor/MPQ.h @@ -0,0 +1,88 @@ +#ifndef MPQ_H +#define MPQ_H + +#include "libmpq/mpq.h" +#include "Define.h" +#include <string> +#include <ctype.h> +#include <vector> +#include <iostream> +#include <deque> + +class MPQArchive +{ + +public: + mpq_archive_s *mpq_a; + + std::vector<std::string> Files; + + MPQArchive(const char* filename); + void close(); + + void GetFileListTo(std::vector<std::string>& filelist) { + uint32_t filenum; + if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return; + libmpq__off_t size, transferred; + libmpq__file_unpacked_size(mpq_a, filenum, &size); + + char *buffer = new char[size]; + + libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); + + char seps[] = "\n"; + char* token; + + token = strtok( buffer, seps ); + uint32 counter = 0; + while ((token != NULL) && (counter < size)) { + //cout << token << endl; + token[strlen(token) - 1] = 0; + std::string s = token; + filelist.push_back(s); + counter += strlen(token) + 2; + token = strtok(NULL, seps); + } + + delete[] buffer; + } +}; + +class MPQFile +{ + //MPQHANDLE handle; + bool eof; + char *buffer; + libmpq__off_t pointer,size; + + // disable copying + MPQFile(const MPQFile& /*f*/) {} + void operator=(const MPQFile& /*f*/) {} + +public: + MPQFile(const char* filename); // filenames are not case sensitive + ~MPQFile() { close(); } + size_t Read(void* dest, size_t bytes); + FILE* GetFileStream(); + size_t getSize() { return size; } + size_t getPos() { return pointer; } + char* getBuffer() { return buffer; } + char* getPointer() { return buffer + pointer; } + bool isEof() { return eof; } + void seek(int offset); + void seekRelative(int offset); + void close(); +}; + +inline void flipcc(char *fcc) +{ + char t; + t=fcc[0]; + fcc[0]=fcc[3]; + fcc[3]=t; + t=fcc[1]; + fcc[1]=fcc[2]; + fcc[2]=t; +} + +#endif diff --git a/src/tools/mesh_extractor/MPQManager.cpp b/src/tools/mesh_extractor/MPQManager.cpp new file mode 100644 index 00000000000..91b9c121c89 --- /dev/null +++ b/src/tools/mesh_extractor/MPQManager.cpp @@ -0,0 +1,108 @@ +#include "MPQManager.h" +#include "MPQ.h" +#include "DBC.h" +#include "Utils.h" +#include <ace/Guard_T.h> + +char const* MPQManager::Files[] = { + "common.MPQ", + "common-2.MPQ", + "expansion.MPQ", + "lichking.MPQ", + "patch.MPQ", + "patch-2.MPQ", + "patch-3.MPQ" +}; + +char const* MPQManager::Languages[] = { "enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" }; + +void MPQManager::Initialize() +{ + InitializeDBC(); + uint32 size = sizeof(Files) / sizeof(char*); + for (uint32 i = 0; i < size; ++i) + { + MPQArchive* arc = new MPQArchive(std::string("Data/" + std::string(Files[i])).c_str()); + Archives.push_front(arc); + printf("Opened %s\n", Files[i]); + } +} + +void MPQManager::InitializeDBC() +{ + BaseLocale = -1; + std::string fileName; + uint32 size = sizeof(Languages) / sizeof(char*); + MPQArchive* _baseLocale = NULL; + for (uint32 i = 0; i < size; ++i) + { + std::string _fileName = "Data/" + std::string(Languages[i]) + "/locale-" + std::string(Languages[i]) + ".MPQ"; + FILE* file = fopen(_fileName.c_str(), "rb"); + if (file) + { + if (BaseLocale == -1) + { + BaseLocale = i; + _baseLocale = new MPQArchive(_fileName.c_str()); + fileName = _fileName; + LocaleFiles[i] = _baseLocale; + } + else + LocaleFiles[i] = new MPQArchive(_fileName.c_str()); + + AvailableLocales.insert(i); + printf("Detected locale: %s\n", Languages[i]); + } + } + Archives.push_front(_baseLocale); + if (BaseLocale == -1) + { + printf("No locale data detected\n"); + ASSERT(false); + } + else + printf("Using default locale: %s\n", Languages[BaseLocale]); +} + +FILE* MPQManager::GetFile( std::string path ) +{ + ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); + MPQFile file(path.c_str()); + if (file.isEof()) + return NULL; + return file.GetFileStream(); +} + +DBC* MPQManager::GetDBC( std::string name ) +{ + std::string path = "DBFilesClient\\" + name + ".dbc"; + return new DBC(GetFile(path)); +} + +FILE* MPQManager::GetFileFrom( std::string path, MPQArchive* file ) +{ + ACE_GUARD_RETURN(ACE_Thread_Mutex, g, mutex, NULL); + mpq_archive* mpq_a = file->mpq_a; + + uint32_t filenum; + if(libmpq__file_number(mpq_a, path.c_str(), &filenum)) + return NULL; + + libmpq__off_t transferred; + libmpq__off_t size = 0; + libmpq__file_unpacked_size(mpq_a, filenum, &size); + + // HACK: in patch.mpq some files don't want to open and give 1 for filesize + if (size <= 1) + return NULL; + + uint8* buffer = new uint8[size]; + + //libmpq_file_getdata + libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred); + + // Pack the return into a FILE stream + FILE* ret = tmpfile(); + fwrite(buffer, sizeof(uint8), size, ret); + return ret; +} diff --git a/src/tools/mesh_extractor/MPQManager.h b/src/tools/mesh_extractor/MPQManager.h new file mode 100644 index 00000000000..2f49ad258a5 --- /dev/null +++ b/src/tools/mesh_extractor/MPQManager.h @@ -0,0 +1,36 @@ +#ifndef MPQ_MANAGER_H +#define MPQ_MANAGER_H + +#include "MPQ.h" +#include <ace/Synch.h> +#include <set> +#include <map> + +class DBC; +class MPQManager +{ +public: + MPQManager() {} + ~MPQManager() {} + + void Initialize(); + FILE* GetFile(std::string path); + FILE* GetFileFrom(std::string path, MPQArchive* file); + DBC* GetDBC(std::string name); + std::vector<std::string> GetAllFiles(std::string extension); + + std::deque<MPQArchive*> Archives; + int32 BaseLocale; + std::set<uint32> AvailableLocales; + std::map<uint32, MPQArchive*> LocaleFiles; + + static char const* Files[]; + static char const* Languages[]; +protected: + void InitializeDBC(); +private: + ACE_Thread_Mutex mutex; +}; + +extern MPQManager* MPQHandler; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/MapChunk.cpp b/src/tools/mesh_extractor/MapChunk.cpp new file mode 100644 index 00000000000..8fe40773d43 --- /dev/null +++ b/src/tools/mesh_extractor/MapChunk.cpp @@ -0,0 +1,74 @@ +#include "MapChunk.h" +#include "ADT.h" +#include "LiquidHandler.h" + +MapChunk::MapChunk( ADT* _adt, Chunk* chunk ) : Adt(_adt), Source(chunk) +{ + FILE* stream = chunk->GetStream(); + Header.Read(stream); + fseek(stream, chunk->Offset, SEEK_SET); + Index = Header.IndexX + Header.IndexY * 16; + GenerateVertices(stream); +} + +void MapChunk::GenerateTriangles() +{ + Triangles.reserve(256); + for (int y = 0; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + if (HasHole(Header.Holes, x / 2, y / 2)) + continue; + + uint32 topLeft = (17 * y) + x; + uint32 topRight = (17 * y) + x + 1; + uint32 bottomLeft = (17 * (y + 1)) + x; + uint32 bottomRight = (17 * (y + 1)) + x + 1; + uint32 center = (17 * y) + 9 + x; + + Constants::TriangleType triangleType = Constants::TRIANGLE_TYPE_TERRAIN; + if (Adt->_LiquidHandler && !Adt->_LiquidHandler->MCNKData.empty()) + { + MCNKLiquidData& data = Adt->_LiquidHandler->MCNKData[Index]; + float maxHeight = std::max( + std::max( + std::max(std::max(Vertices[topLeft].z, Vertices[topRight].z), Vertices[bottomLeft].z), + Vertices[bottomRight].z), Vertices[center].z); + if (data.IsWater(x, y, maxHeight)) + triangleType = Constants::TRIANGLE_TYPE_WATER; + } + + Triangles.push_back(Triangle<uint8>(triangleType, topRight, topLeft, center)); + Triangles.push_back(Triangle<uint8>(triangleType, topLeft, bottomLeft, center)); + Triangles.push_back(Triangle<uint8>(triangleType, bottomLeft, bottomRight, center)); + Triangles.push_back(Triangle<uint8>(triangleType, bottomRight, topRight, center)); + } + } +} + +void MapChunk::GenerateVertices( FILE* stream ) +{ + fseek(stream, Header.OffsetMCVT, SEEK_CUR); + Vertices.reserve(125); + + for (int j = 0; j < 17; j++) + { + int values = j % 2 ? 8 : 9; + for (int i = 0; i < values; i++) + { + float tmp; + if (fread(&tmp, sizeof(float), 1, stream) != 1) + printf("MapChunk::GenerateVertices: Failed to read some data expected 1, read 0\n"); + Vector3 vert(Header.Position.x - (j * (Constants::UnitSize * 0.5f)), Header.Position.y - (i * Constants::UnitSize), Header.Position.z + tmp); + if (values == 8) + vert.y -= Constants::UnitSize * 0.5f; + Vertices.push_back(vert); + } + } +} + +bool MapChunk::HasHole( uint32 map, int x, int y ) +{ + return (map & 0x0000FFFF) & ((1 << x) << (y << 2)); +} diff --git a/src/tools/mesh_extractor/MapChunk.h b/src/tools/mesh_extractor/MapChunk.h new file mode 100644 index 00000000000..e7d835ae0e3 --- /dev/null +++ b/src/tools/mesh_extractor/MapChunk.h @@ -0,0 +1,24 @@ +#ifndef MAPCHUNK_H +#define MAPCHUNK_H +#include "Chunk.h" +#include "Utils.h" +#include "Constants.h" +#include <vector> +class ADT; + +class MapChunk +{ +public: + MapChunk(ADT* _adt, Chunk* chunk); + + void GenerateTriangles(); + void GenerateVertices(FILE* stream); + static bool HasHole(uint32 map, int x, int y); + ADT* Adt; + Chunk* Source; + MapChunkHeader Header; + std::vector<Vector3> Vertices; + std::vector<Triangle<uint8> > Triangles; + int32 Index; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/MeshExtractor.cpp b/src/tools/mesh_extractor/MeshExtractor.cpp new file mode 100644 index 00000000000..e06f44c7125 --- /dev/null +++ b/src/tools/mesh_extractor/MeshExtractor.cpp @@ -0,0 +1,428 @@ +#include "MPQManager.h" +#include "WDT.h" +#include "ContinentBuilder.h" +#include "Cache.h" +#include "DBC.h" +#include "Constants.h" +#include "Model.h" + +#include "Recast.h" +#include "DetourNavMesh.h" +#include "DetourNavMeshQuery.h" + +#include <set> + +MPQManager* MPQHandler; +CacheClass* Cache; + +void ExtractMMaps(std::set<uint32>& mapIds, uint32 threads, bool debug) +{ + DBC* dbc = MPQHandler->GetDBC("Map"); + for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr) + { + uint32 mapId = (*itr)->Values[0]; + + // Skip this map if a list of specific maps was provided and this one is not contained in it. + if (!mapIds.empty() && mapIds.find(mapId) == mapIds.end()) + continue; + + std::string name = (*itr)->GetString(1); + WDT wdt("World\\maps\\" + name + "\\" + name + ".wdt"); + if (!wdt.IsValid || wdt.IsGlobalModel) + continue; + printf("Building %s MapId %u\n", name.c_str(), mapId); + ContinentBuilder builder(name, mapId, &wdt, threads); + builder.Build(debug); + } +} + +void ExtractDBCs() +{ + printf("Extracting DBCs\n"); + // Create the filesystem structure + std::string baseDBCPath = "dbc/"; + Utils::CreateDir(baseDBCPath); + + // Populate list of DBC files + std::set<std::string> DBCFiles; + for (std::vector<std::string>::iterator itr = MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.begin(); itr != MPQHandler->LocaleFiles[MPQHandler->BaseLocale]->Files.end(); ++itr) + if (itr->rfind(".dbc") == itr->length() - strlen(".dbc")) + DBCFiles.insert(*itr); + + // Iterate over all available locales + for (std::set<uint32>::iterator itr = MPQHandler->AvailableLocales.begin(); itr != MPQHandler->AvailableLocales.end(); ++itr) + { + printf("Extracting DBCs for locale %s\n", MPQManager::Languages[*itr]); + std::string path = baseDBCPath; + if (*itr != uint32(MPQHandler->BaseLocale)) + { + path += std::string(MPQManager::Languages[*itr]) + "/"; + Utils::CreateDir(path); + } + + std::string component = "component.wow-" + std::string(MPQManager::Languages[*itr]) + ".txt"; + // Extract the component file + Utils::SaveToDisk(MPQHandler->GetFile(component), path + component); + // Extract the DBC files for the given locale + for (std::set<std::string>::iterator itr2 = DBCFiles.begin(); itr2 != DBCFiles.end(); ++itr2) + Utils::SaveToDisk(MPQHandler->GetFileFrom(*itr2, MPQHandler->LocaleFiles[*itr]), path + (itr2->c_str() + strlen("DBFilesClient\\"))); + } + printf("DBC extraction finished!\n"); +} + +void ExtractGameobjectModels() +{ + Constants::ToWoWCoords = true; + printf("Extracting GameObject models\n"); + + std::string baseBuildingsPath = "Buildings/"; + std::string baseVmapsPath = "vmaps/"; + Utils::CreateDir(baseVmapsPath); + Utils::CreateDir(baseBuildingsPath); + + FILE* modelList = fopen((baseVmapsPath + "GameObjectModels.list").c_str(), "wb"); + if (!modelList) + { + printf("Could not create file vmaps/GameObjectModels.list, please make sure that you have the write permissions in the folder\n"); + return; + } + + DBC* dbc = MPQHandler->GetDBC("GameObjectDisplayInfo"); + for (std::vector<Record*>::iterator itr = dbc->Records.begin(); itr != dbc->Records.end(); ++itr) + { + std::string path = (*itr)->GetString(1); + std::string fileName = Utils::GetPlainName(path.c_str()); + std::string extension = Utils::GetExtension(fileName); + // Convert the extension to lowercase + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + if (extension == "mdx" || extension == "m2") + { + fileName = Utils::FixModelPath(fileName); + Model model(path); + + if (model.IsBad) + continue; + + FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb"); + if (!output) + { + printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str()); + continue; + } + // Placeholder for 0 values + int Nop[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + fwrite(Constants::VMAPMagic, 8, 1, output); + uint32 numVerts = model.Header.CountBoundingVertices; + fwrite(&numVerts, sizeof(uint32), 1, output); + uint32 numGroups = 1; + fwrite(&numGroups, sizeof(uint32), 1, output); + fwrite(Nop, 4 * 3 , 1, output); // rootwmoid, flags, groupid + fwrite(Nop, sizeof(float), 3 * 2, output);//bbox, only needed for WMO currently + fwrite(Nop, 4, 1, output);// liquidflags + fwrite("GRP ", 4, 1, output); + + uint32 branches = 1; + uint32 wsize = sizeof(branches) + sizeof(uint32) * branches; + fwrite(&wsize, sizeof(uint32), 1, output); + fwrite(&branches, sizeof(branches), 1, output); + uint32 numTris = model.Header.CountBoundingTriangles; + fwrite(&numTris, sizeof(uint32), 1, output); + fwrite("INDX", 4, 1, output); + wsize = sizeof(uint32) + sizeof(unsigned short) * numTris; + fwrite(&wsize, sizeof(int), 1, output); + fwrite(&numTris, sizeof(uint32), 1, output); + uint16* indices = new uint16[numTris]; + + if (numTris > 0) + { + uint32 i = 0; + for (std::vector<Triangle<uint16> >::iterator itr2 = model.Triangles.begin(); itr2 != model.Triangles.end(); ++itr2, ++i) + { + indices[i * 3 + 0] = itr2->V0; + indices[i * 3 + 1] = itr2->V1; + indices[i * 3 + 2] = itr2->V2; + } + fwrite(indices, sizeof(uint16), numTris, output); + } + + + fwrite("VERT", 4, 1, output); + wsize = sizeof(int) + sizeof(float) * 3 * numVerts; + fwrite(&wsize, sizeof(int), 1, output); + fwrite(&numVerts, sizeof(int), 1, output); + float* vertices = new float[numVerts*3]; + + if (numVerts > 0) + { + uint32 i = 0; + for (std::vector<Vector3>::iterator itr2 = model.Vertices.begin(); itr2 != model.Vertices.end(); ++itr2, ++i) + { + vertices[i * 3 + 0] = itr2->x; + vertices[i * 3 + 1] = itr2->y; + vertices[i * 3 + 2] = itr2->z; + } + + fwrite(vertices, sizeof(float), numVerts * 3, output); + } + + fclose(output); + delete[] indices; + delete[] vertices; + + uint32 displayId = (*itr)->Values[0]; + uint32 pathLength = fileName.size(); + fwrite(&displayId, sizeof(uint32), 1, modelList); + fwrite(&pathLength, sizeof(uint32), 1, modelList); + fwrite(fileName.c_str(), sizeof(char), pathLength, modelList); + } + else if (extension == "wmo") + { + WorldModelRoot model(path); + + FILE* output = fopen((baseBuildingsPath + fileName).c_str(), "wb"); + if (!output) + { + printf("Could not create file %s, please check that you have write permissions\n", (baseBuildingsPath + fileName).c_str()); + continue; + } + + fwrite(Constants::VMAPMagic, 1, 8, output); + uint32 numVertices = 0; + fwrite(&numVertices, sizeof(uint32), 1, output); // will be filled later + fwrite(&model.Header.CountGroups, sizeof(uint32), 1, output); + fwrite(&model.Header.WmoId, sizeof(uint32), 1, output); + + for (std::vector<WorldModelGroup>::iterator itr2 = model.Groups.begin(); itr2 != model.Groups.end(); ++itr2) + { + fwrite(&itr2->Header.Flags, sizeof(uint32), 1, output); + fwrite(&itr2->Header.WmoId, sizeof(uint32), 1, output); + fwrite(&itr2->Header.BoundingBox[0], sizeof(uint32), 1, output); + fwrite(&itr2->Header.BoundingBox[1], sizeof(uint32), 1, output); + uint32 LiquidFlags = itr2->HasLiquidData ? 1 : 0; + fwrite(&LiquidFlags, sizeof(uint32), 1, output); + + fwrite("GRP ", sizeof(char), 4, output); + uint32 k = 0; + uint32 mobaBatch = itr2->MOBALength / 12; + uint32* MobaEx = new uint32[mobaBatch*4]; + + for(uint32 i = 8; i < itr2->MOBALength; i += 12) + MobaEx[k++] = itr2->MOBA[i]; + + int mobaSizeGrp = mobaBatch * 4 + 4; + fwrite(&mobaSizeGrp, 4, 1, output); + fwrite(&mobaBatch, 4, 1, output); + fwrite(MobaEx, 4, k, output); + delete[] MobaEx; + + // Note: still not finished + } + + fclose(output); + } + } + + fclose(modelList); + printf("GameObject models extraction finished!"); + Constants::ToWoWCoords = false; +} + +bool HandleArgs(int argc, char** argv, uint32& threads, std::set<uint32>& mapList, bool& debugOutput, uint32& extractFlags) +{ + char* param = NULL; + extractFlags = 0; + + for (int i = 1; i < argc; ++i) + { + if (strcmp(argv[i], "--threads") == 0) + { + param = argv[++i]; + if (!param) + return false; + + threads = atoi(param); + printf("Using %i threads\n", threads); + } + else if (strcmp(argv[i], "--maps") == 0) + { + param = argv[++i]; + if (!param) + return false; + + char* copy = strdup(param); + char* token = strtok(copy, ","); + while (token) + { + mapList.insert(atoi(token)); + token = strtok(NULL, ","); + } + + printf("Extracting only provided list of maps (%u).\n", uint32(mapList.size())); + } + else if (strcmp(argv[i], "--debug") == 0) + { + param = argv[++i]; + if (!param) + return false; + debugOutput = atoi(param); + if (debugOutput) + printf("Output will contain debug information (.obj files)\n"); + } + else if (strcmp(argv[i], "--extract") == 0) + { + param = argv[++i]; + if (!param) + return false; + + extractFlags = atoi(param); + + if (!(extractFlags & Constants::EXTRACT_FLAG_ALLOWED)) // Tried to use an invalid flag + return false; + + printf("Detected flags: \n"); + printf("* Extract DBCs: %s\n", (extractFlags & Constants::EXTRACT_FLAG_DBC) ? "Yes" : "No"); + printf("* Extract Maps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MAPS) ? "Yes" : "No"); + printf("* Extract VMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_VMAPS) ? "Yes" : "No"); + printf("* Extract GameObject Models: %s\n", (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) ? "Yes" : "No"); + printf("* Extract MMaps: %s\n", (extractFlags & Constants::EXTRACT_FLAG_MMAPS) ? "Yes" : "No"); + } + } + return true; +} + +void PrintUsage() +{ + printf("MeshExtractor help.\n"); + printf("* Use \"--threads <number>\" to specify <number> threads, default to 4 (Only available when extracting MMaps)\n"); + printf("* Use \"--maps a,b,c,d,e\" to extract only the maps specified (Do not use spaces)\n"); + printf("* Use \"--debug 1\" to generate debug information of the tiles (Only available when extracting MMaps)\n"); + printf("* Use \"--extract X\" to extract the data specified by the flag X (Note: You can combine the flags with the bitwise OR operator |). Available flags are: \n"); + { + printf("- %u to extract DBCs\n", Constants::EXTRACT_FLAG_DBC); + printf("- %u to extract Maps (Not yet implemented)\n", Constants::EXTRACT_FLAG_MAPS); + printf("- %u to extract VMaps (Not yet implemented)\n", Constants::EXTRACT_FLAG_VMAPS); + printf("- %u to extract GameObject models (Not yet finished, you need to run VMapAssembler on the extracted files)\n", Constants::EXTRACT_FLAG_GOB_MODELS); + printf("- %u to extract MMaps (Not yet finished)\n", Constants::EXTRACT_FLAG_MMAPS); + } +} + +void LoadTile(dtNavMesh*& navMesh, const char* tile) +{ + FILE* f = fopen(tile, "rb"); + MmapTileHeader header; + + if (fread(&header, sizeof(MmapTileHeader), 1, f) != 1) + return; + + uint8* nav = new uint8[header.size]; + if (fread(nav, header.size, 1, f) != 1) + return; + + navMesh->addTile(nav, header.size, DT_TILE_FREE_DATA, 0, NULL); + + fclose(f); +} + +int main(int argc, char* argv[]) +{ + if (!system("pause")) + { + printf("main: Error in system call to pause\n"); + return -1; + } + + uint32 threads = 4, extractFlags = 0; + std::set<uint32> mapIds; + bool debug = false; + + if (!HandleArgs(argc, argv, threads, mapIds, debug, extractFlags)) + { + PrintUsage(); + return -1; + } + + Cache = new CacheClass(); + MPQHandler = new MPQManager(); + MPQHandler->Initialize(); + + if (extractFlags & Constants::EXTRACT_FLAG_DBC) + ExtractDBCs(); + + if (extractFlags & Constants::EXTRACT_FLAG_MMAPS) + ExtractMMaps(mapIds, threads, debug); + + if (extractFlags & Constants::EXTRACT_FLAG_GOB_MODELS) + ExtractGameobjectModels(); + + if (extractFlags & Constants::EXTRACT_FLAG_TEST) + { + float start[] = { 0.0f, 0.0f, 0.0f }; + float end[] = { 0.0f, 0.0f, 0.0f }; + + // + float m_spos[3]; + m_spos[0] = -1.0f * start[1]; + m_spos[1] = start[2]; + m_spos[2] = -1.0f * start[0]; + + // + float m_epos[3]; + m_epos[0] = -1.0f * end[1]; + m_epos[1] = end[2]; + m_epos[2] = -1.0f * end[0]; + + // + dtQueryFilter m_filter; + m_filter.setIncludeFlags(0xffff) ; + m_filter.setExcludeFlags(0); + + // + float m_polyPickExt[3]; + m_polyPickExt[0] = 2; + m_polyPickExt[1] = 4; + m_polyPickExt[2] = 2; + + // + dtPolyRef m_startRef; + dtPolyRef m_endRef; + + FILE* mmap = fopen(".mmap", "rb"); + dtNavMeshParams params; + int count = fread(¶ms, sizeof(dtNavMeshParams), 1, mmap); + fclose(mmap); + if (count != 1) + { + printf("main: Error reading from .mmap\n"); + return 0; + } + + dtNavMesh* navMesh = new dtNavMesh(); + dtNavMeshQuery* navMeshQuery = new dtNavMeshQuery(); + + navMesh->init(¶ms); + LoadTile(navMesh, ".mmtile"); + LoadTile(navMesh, ".mmtile"); + LoadTile(navMesh, ".mmtile"); + LoadTile(navMesh, ".mmtile"); + LoadTile(navMesh, ".mmtile"); + LoadTile(navMesh, ".mmtile"); + + navMeshQuery->init(navMesh, 2048); + + float nearestPt[3]; + + navMeshQuery->findNearestPoly(m_spos, m_polyPickExt, &m_filter, &m_startRef, nearestPt); + navMeshQuery->findNearestPoly(m_epos, m_polyPickExt, &m_filter, &m_endRef, nearestPt); + + if ( !m_startRef || !m_endRef ) + { + std::cerr << "Could not find any nearby poly's (" << m_startRef << "," << m_endRef << ")" << std::endl; + return 0; + } + + printf("Found!"); + } + + return 0; +} diff --git a/src/tools/mesh_extractor/Model.cpp b/src/tools/mesh_extractor/Model.cpp new file mode 100644 index 00000000000..77b1adbeaa0 --- /dev/null +++ b/src/tools/mesh_extractor/Model.cpp @@ -0,0 +1,67 @@ +#include "Model.h" +#include "MPQManager.h" +#include "Utils.h" + +Model::Model( std::string path ) : IsCollidable(false), IsBad(false) +{ + Stream = MPQHandler->GetFile(Utils::FixModelPath(path)); + if (!Stream) + { + IsBad = true; + return; + } + Header.Read(Stream); + if (Header.OffsetBoundingNormals > 0 && Header.OffsetBoundingVertices > 0 && + Header.OffsetBoundingTriangles > 0 && Header.BoundingRadius > 0.0f) + { + IsCollidable = true; + ReadVertices(Stream); + ReadBoundingNormals(Stream); + ReadBoundingTriangles(Stream); + } +} + +Model::~Model() +{ + if (Stream) + fclose(Stream); +} + +void Model::ReadVertices( FILE* stream ) +{ + fseek(stream, Header.OffsetBoundingVertices, SEEK_SET); + Vertices.reserve(Header.CountBoundingVertices); + for (uint32 i = 0; i < Header.CountBoundingVertices; ++i) + { + Vertices.push_back(Vector3::Read(stream)); + if (Constants::ToWoWCoords) + Vertices[i] = Utils::ToWoWCoords(Vertices[i]); + } +} + +void Model::ReadBoundingTriangles( FILE* stream ) +{ + fseek(stream, Header.OffsetBoundingTriangles, SEEK_SET); + Triangles.reserve(Header.CountBoundingTriangles / 3); + for (uint32 i = 0; i < Header.CountBoundingTriangles / 3; i++) + { + Triangle<uint16> tri; + tri.Type = Constants::TRIANGLE_TYPE_DOODAD; + int count = 0; + count += fread(&tri.V0, sizeof(uint16), 1, stream); + count += fread(&tri.V1, sizeof(uint16), 1, stream); + count += fread(&tri.V2, sizeof(uint16), 1, stream); + if (count != 3) + printf("Model::ReadBoundingTriangles: Error reading data, expected 3, read %d\n", count); + Triangles.push_back(tri); + } +} + +void Model::ReadBoundingNormals( FILE* stream ) +{ + fseek(stream, Header.OffsetBoundingNormals, SEEK_SET); + Normals.reserve(Header.CountBoundingNormals); + for (uint32 i = 0; i < Header.CountBoundingNormals; i++) + Normals.push_back(Vector3::Read(stream)); +} + diff --git a/src/tools/mesh_extractor/Model.h b/src/tools/mesh_extractor/Model.h new file mode 100644 index 00000000000..ea9331e7c30 --- /dev/null +++ b/src/tools/mesh_extractor/Model.h @@ -0,0 +1,23 @@ +#ifndef MODEL_H +#define MODEL_H +#include <vector> +#include "Utils.h" + +class Model +{ +public: + Model(std::string path); + ~Model(); + + void ReadVertices(FILE* stream); + void ReadBoundingTriangles(FILE* stream); + void ReadBoundingNormals(FILE* stream); + ModelHeader Header; + std::vector<Vector3> Vertices; + std::vector<Vector3> Normals; + std::vector<Triangle<uint16> > Triangles; + bool IsCollidable; + FILE* Stream; + bool IsBad; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/ObjectDataHandler.cpp b/src/tools/mesh_extractor/ObjectDataHandler.cpp new file mode 100644 index 00000000000..789efc6d62c --- /dev/null +++ b/src/tools/mesh_extractor/ObjectDataHandler.cpp @@ -0,0 +1,21 @@ +#include "ObjectDataHandler.h" +#include "Chunk.h" +#include "ADT.h" +#include "ChunkedData.h" + +void ObjectDataHandler::ProcessMapChunk( MapChunk* chunk ) +{ + if (!Source->HasObjectData) + return; + // fuck it blizzard, why is this crap necessary? + int32 firstIndex = Source->ObjectData->GetFirstIndex("MCNK"); + if (firstIndex == -1) + return; + if (uint32(firstIndex + chunk->Index) > Source->ObjectData->Chunks.size()) + return; + Chunk* ourChunk = Source->ObjectData->Chunks[firstIndex + chunk->Index]; + if (ourChunk->Length == 0) + return; + ChunkedData* subChunks = new ChunkedData(ourChunk->GetStream(), ourChunk->Length, 2); + ProcessInternal(subChunks); +} diff --git a/src/tools/mesh_extractor/ObjectDataHandler.h b/src/tools/mesh_extractor/ObjectDataHandler.h new file mode 100644 index 00000000000..75b4e45700c --- /dev/null +++ b/src/tools/mesh_extractor/ObjectDataHandler.h @@ -0,0 +1,15 @@ +#ifndef ODATA_HNDL_H +#define ODATA_HNDL_H +#include "ADT.h" +#include "MapChunk.h" + +class ObjectDataHandler +{ +public: + ObjectDataHandler(ADT* _adt) : Source(_adt) {} + + void ProcessMapChunk(MapChunk* chunk); + virtual void ProcessInternal(ChunkedData* data) = 0; + ADT* Source; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/TileBuilder.cpp b/src/tools/mesh_extractor/TileBuilder.cpp new file mode 100644 index 00000000000..9bb9b11619f --- /dev/null +++ b/src/tools/mesh_extractor/TileBuilder.cpp @@ -0,0 +1,311 @@ +#include "ContinentBuilder.h" +#include "TileBuilder.h" +#include "Geometry.h" +#include "Constants.h" +#include "Utils.h" +#include "Cache.h" +#include "ADT.h" +#include "WDT.h" +#include "Recast.h" +#include "RecastAlloc.h" +#include "DetourNavMeshBuilder.h" + +#include <ace/Synch.h> + +TileBuilder::TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId) : + World(world), X(x), Y(y), MapId(mapId), _Geometry(NULL), DataSize(0), cBuilder(_cBuilder) +{ + /* + Test, non-working values + // Cell Size = TileSize / TileVoxelSize + // 1800 = TileVoxelSize + Config.cs = Constants::TileSize / 1800; + // Cell Height + Config.ch = 0.4f; + // Min Region Area = 20^2 + Config.minRegionArea = 20*20; + // Merge Region Area = 40^2 + Config.mergeRegionArea = 40*40; + Config.tileSize = Constants::TileSize / 4; + Config.walkableSlopeAngle = 50.0f; + Config.detailSampleDist = 3.0f; + Config.detailSampleMaxError = 1.25f; + Config.walkableClimb = floorf(1.0f / Config.ch); + Config.walkableHeight = ceilf(1.652778f / Config.ch); + Config.walkableRadius = ceilf(0.2951389f / Config.cs); + Config.maxEdgeLen = Config.walkableRadius * 8; + Config.borderSize = Config.walkableRadius + 4; + Config.width = 1800 + Config.borderSize * 2; + Config.height = 1800 + Config.borderSize * 2; + Config.maxVertsPerPoly = 6; + Config.maxSimplificationError = 1.3f; + */ + + // All are in UNIT metrics! + memset(&Config, 0, sizeof(rcConfig)); + + Config.maxVertsPerPoly = DT_VERTS_PER_POLYGON; + Config.cs = Constants::BaseUnitDim; + Config.ch = Constants::BaseUnitDim; + Config.walkableSlopeAngle = 60.0f; + Config.tileSize = Constants::VertexPerTile; + Config.walkableRadius = 1; + Config.borderSize = Config.walkableRadius + 3; + Config.maxEdgeLen = Constants::VertexPerTile + 1; //anything bigger than tileSize + Config.walkableHeight = 3; + Config.walkableClimb = 2; // 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; + + Context = new rcContext; +} + +void TileBuilder::CalculateTileBounds( float*& bmin, float*& bmax, dtNavMeshParams& /*navMeshParams*/ ) +{ + bmin = new float[3]; + bmax = new float[3]; + bmin[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * X); + bmin[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * Y); + bmax[0] = Constants::Origin[0] /*navMeshParams.orig[0]*/ + (Constants::TileSize * (X + 1)); + bmax[2] = Constants::Origin[2] /*navMeshParams.orig[2]*/ + (Constants::TileSize * (Y + 1)); +} + +uint8* TileBuilder::Build(bool dbg, dtNavMeshParams& navMeshParams) +{ + _Geometry = new Geometry(); + _Geometry->Transform = true; + ADT* adt = new ADT(Utils::GetAdtPath(World, X, Y)); + adt->Read(); + _Geometry->AddAdt(adt); + delete adt; + + if (_Geometry->Vertices.empty() && _Geometry->Triangles.empty()) + return NULL; + + // again, we load everything - wasteful but who cares + for (int ty = Y - 2; ty <= Y + 2; ty++) + { + for (int tx = X - 2; tx <= X + 2; tx++) + { + // don't load main tile again + if (tx == X && ty == Y) + continue; + + ADT* _adt = new ADT(Utils::GetAdtPath(World, tx, ty)); + // If this condition is met, it means that this wdt does not contain the ADT + if (!_adt->Data->Stream) + { + delete _adt; + continue; + } + _adt->Read(); + _Geometry->AddAdt(_adt); + delete _adt; + } + } + + if (dbg) + { + char buff[100]; + sprintf(buff, "mmaps/%s_%02u%02u.obj", World.c_str(), Y, X); + FILE* debug = fopen(buff, "wb"); + for (uint32 i = 0; i < _Geometry->Vertices.size(); ++i) + fprintf(debug, "v %f %f %f\n", _Geometry->Vertices[i].x, _Geometry->Vertices[i].y, _Geometry->Vertices[i].z); + for (uint32 i = 0; i < _Geometry->Triangles.size(); ++i) + fprintf(debug, "f %i %i %i\n", _Geometry->Triangles[i].V0 + 1, _Geometry->Triangles[i].V1 + 1, _Geometry->Triangles[i].V2 + 1); + fclose(debug); + } + + uint32 numVerts = _Geometry->Vertices.size(); + uint32 numTris = _Geometry->Triangles.size(); + float* vertices; + int* triangles; + uint8* areas; + _Geometry->GetRawData(vertices, triangles, areas); + _Geometry->Vertices.clear(); + _Geometry->Triangles.clear(); + + + rcVcopy(Config.bmin, cBuilder->bmin); + rcVcopy(Config.bmax, cBuilder->bmax); + + // this sets the dimensions of the heightfield - should maybe happen before border padding + rcCalcGridSize(Config.bmin, Config.bmax, Config.cs, &Config.width, &Config.height); + + // 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*[Constants::TilesPerMap * Constants::TilesPerMap]; + rcPolyMeshDetail** dmmerge = new rcPolyMeshDetail*[Constants::TilesPerMap * Constants::TilesPerMap]; + + int nmerge = 0; + for (int y = 0; y < Constants::TilesPerMap; ++y) + { + for (int x = 0; x < Constants::TilesPerMap; ++x) + { + // Calculate the per tile bounding box. + tileCfg.bmin[0] = Config.bmin[0] + float(x * Config.tileSize - Config.borderSize) * Config.cs; + tileCfg.bmin[2] = Config.bmin[2] + float(y * Config.tileSize - Config.borderSize) * Config.cs; + tileCfg.bmax[0] = Config.bmin[0] + float((x + 1) * Config.tileSize + Config.borderSize) * Config.cs; + tileCfg.bmax[2] = Config.bmin[2] + float((y + 1) * Config.tileSize + Config.borderSize) * Config.cs; + + + rcHeightfield* hf = rcAllocHeightfield(); + rcCreateHeightfield(Context, *hf, tileCfg.width, tileCfg.height, tileCfg.bmin, tileCfg.bmax, tileCfg.cs, tileCfg.ch); + rcClearUnwalkableTriangles(Context, tileCfg.walkableSlopeAngle, vertices, numVerts, triangles, numTris, areas); + rcRasterizeTriangles(Context, vertices, numVerts, triangles, areas, numTris, *hf, Config.walkableClimb); + + // Once all geometry is rasterized, we do initial pass of filtering to + // remove unwanted overhangs caused by the conservative rasterization + // as well as filter spans where the character cannot possibly stand. + rcFilterLowHangingWalkableObstacles(Context, Config.walkableClimb, *hf); + rcFilterLedgeSpans(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf); + rcFilterWalkableLowHeightSpans(Context, tileCfg.walkableHeight, *hf); + + // Compact the heightfield so that it is faster to handle from now on. + // This will result in more cache coherent data as well as the neighbours + // between walkable cells will be calculated. + rcCompactHeightfield* chf = rcAllocCompactHeightfield(); + rcBuildCompactHeightfield(Context, tileCfg.walkableHeight, tileCfg.walkableClimb, *hf, *chf); + + rcFreeHeightField(hf); + + // Erode the walkable area by agent radius. + rcErodeWalkableArea(Context, Config.walkableRadius, *chf); + // Prepare for region partitioning, by calculating distance field along the walkable surface. + rcBuildDistanceField(Context, *chf); + // Partition the walkable surface into simple regions without holes. + rcBuildRegions(Context, *chf, tileCfg.borderSize, tileCfg.minRegionArea, tileCfg.mergeRegionArea); + + // Create contours. + rcContourSet* cset = rcAllocContourSet(); + rcBuildContours(Context, *chf, tileCfg.maxSimplificationError, tileCfg.maxEdgeLen, *cset); + + // Build polygon navmesh from the contours. + rcPolyMesh* pmesh = rcAllocPolyMesh(); + rcBuildPolyMesh(Context, *cset, tileCfg.maxVertsPerPoly, *pmesh); + + // Build detail mesh. + rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); + rcBuildPolyMeshDetail(Context, *pmesh, *chf, tileCfg.detailSampleDist, tileCfg.detailSampleMaxError, *dmesh); + + // Free memory + rcFreeCompactHeightfield(chf); + rcFreeContourSet(cset); + + pmmerge[nmerge] = pmesh; + dmmerge[nmerge] = dmesh; + ++nmerge; + } + } + + rcPolyMesh* pmesh = rcAllocPolyMesh(); + rcMergePolyMeshes(Context, pmmerge, nmerge, *pmesh); + + rcPolyMeshDetail* dmesh = rcAllocPolyMeshDetail(); + rcMergePolyMeshDetails(Context, dmmerge, nmerge, *dmesh); + + delete[] pmmerge; + delete[] dmmerge; + + printf("[%02i,%02i] Meshes merged!\n", X, Y); + + // Remove padding from the polymesh data. (Remove this odditity) + for (int i = 0; i < pmesh->nverts; ++i) + { + unsigned short* v = &pmesh->verts[i * 3]; + v[0] -= (unsigned short)Config.borderSize; + v[2] -= (unsigned short)Config.borderSize; + } + + // Set flags according to area types (e.g. Swim for Water) + for (int i = 0; i < pmesh->npolys; i++) + { + if (pmesh->areas[i] == Constants::POLY_AREA_ROAD || pmesh->areas[i] == Constants::POLY_AREA_TERRAIN) + pmesh->flags[i] = Constants::POLY_FLAG_WALK; + else if (pmesh->areas[i] == Constants::POLY_AREA_WATER) + pmesh->flags[i] = Constants::POLY_FLAG_SWIM; + } + + dtNavMeshCreateParams params; + memset(¶ms, 0, sizeof(params)); + // PolyMesh data + params.verts = pmesh->verts; + params.vertCount = pmesh->nverts; + params.polys = pmesh->polys; + params.polyAreas = pmesh->areas; + params.polyFlags = pmesh->flags; + params.polyCount = pmesh->npolys; + params.nvp = pmesh->nvp; + // PolyMeshDetail data + params.detailMeshes = dmesh->meshes; + params.detailVerts = dmesh->verts; + params.detailVertsCount = dmesh->nverts; + params.detailTris = dmesh->tris; + params.detailTriCount = dmesh->ntris; + rcVcopy(params.bmin, pmesh->bmin); + rcVcopy(params.bmax, pmesh->bmax); + // General settings + params.ch = Config.ch; + params.cs = Config.cs; + params.walkableClimb = Constants::BaseUnitDim * Config.walkableClimb; + params.walkableHeight = Constants::BaseUnitDim * Config.walkableHeight; + params.walkableRadius = Constants::BaseUnitDim * Config.walkableRadius; + params.tileX = (((cBuilder->bmin[0] + cBuilder->bmax[0]) / 2) - navMeshParams.orig[0]) / Constants::TileSize; + params.tileY = (((cBuilder->bmin[2] + cBuilder->bmax[2]) / 2) - navMeshParams.orig[2]) / Constants::TileSize; + + rcVcopy(params.bmin, cBuilder->bmin); + rcVcopy(params.bmax, cBuilder->bmax); + + // Offmesh-connection settings + params.offMeshConCount = 0; // none for now + + params.tileSize = Constants::VertexPerMap; + + if (!params.polyCount || !params.polys || Constants::TilesPerMap * Constants::TilesPerMap == 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("[%02i,%02i] No polygons to build on tile, skipping.\n", X, Y); + rcFreePolyMesh(pmesh); + rcFreePolyMeshDetail(dmesh); + delete areas; + delete triangles; + delete vertices; + return NULL; + } + + int navDataSize; + uint8* navData; + printf("[%02i,%02i] Creating the navmesh with %i vertices, %i polys, %i triangles!\n", X, Y, pmesh->nverts, pmesh->npolys, dmesh->ntris); + bool result = dtCreateNavMeshData(¶ms, &navData, &navDataSize); + + // Free some memory + rcFreePolyMesh(pmesh); + rcFreePolyMeshDetail(dmesh); + delete areas; + delete triangles; + delete vertices; + + if (result) + { + printf("[%02i,%02i] NavMesh created, size %i!\n", X, Y, navDataSize); + DataSize = navDataSize; + return navData; + } + + return NULL; +} + +TileBuilder::~TileBuilder() +{ + delete Context; + delete _Geometry; +} diff --git a/src/tools/mesh_extractor/TileBuilder.h b/src/tools/mesh_extractor/TileBuilder.h new file mode 100644 index 00000000000..40c96f6ec42 --- /dev/null +++ b/src/tools/mesh_extractor/TileBuilder.h @@ -0,0 +1,30 @@ +#ifndef TILE_BUILD_H +#define TILE_BUILD_H +#include <string> +#include "Recast.h" + +#include "Geometry.h" + +class ContinentBuilder; +class WDT; + +class TileBuilder +{ +public: + TileBuilder(ContinentBuilder* _cBuilder, std::string world, int x, int y, uint32 mapId); + ~TileBuilder(); + + void CalculateTileBounds(float*& bmin, float*& bmax, dtNavMeshParams& navMeshParams); + uint8* Build(bool dbg, dtNavMeshParams& navMeshParams); + + std::string World; + int X; + int Y; + int MapId; + rcConfig Config; + rcContext* Context; + Geometry* _Geometry; + uint32 DataSize; + ContinentBuilder* cBuilder; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/Utils.cpp b/src/tools/mesh_extractor/Utils.cpp new file mode 100644 index 00000000000..acb1ed38e27 --- /dev/null +++ b/src/tools/mesh_extractor/Utils.cpp @@ -0,0 +1,568 @@ +#include "Utils.h" +#include "WorldModelHandler.h" +#include "Constants.h" +#include <cstring> +#include "G3D/Matrix4.h" +#include "G3D/Quat.h" + +#ifdef _WIN32 + #include "direct.h" +#else + #include <sys/stat.h> + #include <unistd.h> +#endif + +const float Constants::TileSize = 533.0f + (1/3.0f); +const float Constants::MaxXY = 32.0f * Constants::TileSize; +const float Constants::ChunkSize = Constants::TileSize / 16.0f; +const float Constants::UnitSize = Constants::ChunkSize / 8.0f; +const float Constants::Origin[] = { -Constants::MaxXY, 0.0f, -Constants::MaxXY }; +const float Constants::PI = 3.1415926f; +const float Constants::MaxStandableHeight = 1.5f; +const char* Constants::VMAPMagic = "VMAP041"; +bool Constants::ToWoWCoords = false; +const float Constants::BaseUnitDim = 0.533333f; +const int Constants::VertexPerMap = (Constants::TileSize / Constants::BaseUnitDim) + 0.5f; +const int Constants::VertexPerTile = 40; +const int Constants::TilesPerMap = Constants::VertexPerMap / Constants::VertexPerTile; + +void Utils::CreateDir( const std::string& Path ) +{ +#ifdef _WIN32 + _mkdir( Path.c_str()); +#else + mkdir( Path.c_str(), 0777 ); +#endif +} + +void Utils::Reverse(char word[]) +{ + int len = strlen(word); + for (int i = 0;i < len / 2; i++) + { + word[i] ^= word[len-i-1]; + word[len-i-1] ^= word[i]; + word[i] ^= word[len-i-1]; + } +} + +std::string Utils::ReadString( FILE* file ) +{ + std::string ret; + int i = 0; + while (true) + { + char b; + if (fread(&b, sizeof(char), 1, file) != 1 || b == 0) + break; + ret[i++] = b; + } + return ret; +} + +uint32 Utils::Size( FILE* file ) +{ + // store the old position + uint32 offset = ftell(file); + // Get file size + fseek(file, 0, SEEK_END); + uint32 size = ftell(file); + // reset back to the old position + fseek(file, offset, SEEK_SET); + return size; +} + +Vector3 Utils::ToRecast( Vector3 val ) +{ + return Vector3(-val.y, val.z, -val.x); +} + +std::string Utils::GetAdtPath( std::string world, int x, int y ) +{ + return "World\\Maps\\" + world + "\\" + world + "_" + Utils::ToString(x) + "_" + Utils::ToString(y) + ".adt"; +} + +std::string Utils::FixModelPath( std::string path ) +{ + return Utils::GetPathBase(path) + ".M2"; +} + +G3D::Matrix4 Utils::RotationX(float angle) +{ + float _cos = cos(angle); + float _sin = sin(angle); + G3D::Matrix4 ret = G3D::Matrix4::identity(); + ret[2][2] = _cos; + ret[2][3] = _sin; + ret[3][2] = -_sin; + ret[3][3] = _cos; + return ret; +} + +G3D::Matrix4 Utils::GetTransformation(IDefinition def) +{ + G3D::Matrix4 translation; + if (def.Position.x == 0.0f && def.Position.y == 0.0f && def.Position.z == 0.0f) + translation = G3D::Matrix4::identity(); + else + translation = G3D::Matrix4::translation(-(def.Position.z - Constants::MaxXY), + -(def.Position.x - Constants::MaxXY), def.Position.y); + + G3D::Matrix4 rotation = RotationX(ToRadians(def.Rotation.z)) * RotationY(ToRadians(def.Rotation.x)) * RotationZ(ToRadians(def.Rotation.y + 180)); + if (def.Scale() < 1.0f || def.Scale() > 1.0f) + return G3D::Matrix4::scale(def.Scale()) * rotation * translation; + return rotation * translation; +} + +G3D::Matrix4 Utils::RotationY( float angle ) +{ + float _cos = cos(angle); + float _sin = sin(angle); + G3D::Matrix4 ret = G3D::Matrix4::identity(); + ret[1][1] = _cos; + ret[1][3] = -_sin; + ret[3][1] = _sin; + ret[3][3] = _cos; + return ret; +} + +G3D::Matrix4 Utils::RotationZ( float angle ) +{ + float _cos = cos(angle); + float _sin = sin(angle); + G3D::Matrix4 ret = G3D::Matrix4::identity(); + ret[1][1] = _cos; + ret[1][2] = _sin; + ret[2][1] = -_sin; + ret[2][2] = _cos; + return ret; +} + +float Utils::ToRadians( float degrees ) +{ + return Constants::PI * degrees / 180.0f; +} + +Vector3 Utils::VectorTransform( Vector3 vec, G3D::Matrix4 matrix ) +{ + Vector3 ret; + ret.x = vec.x * matrix[1][1] + vec.y * matrix[2][1] + vec.z * matrix[3][1] + matrix[4][1]; + ret.y = vec.x * matrix[1][2] + vec.y * matrix[2][2] + vec.z * matrix[3][2] + matrix[4][2]; + ret.z = vec.x * matrix[1][3] + vec.y * matrix[2][3] + vec.z * matrix[3][3] + matrix[4][3]; + return ret; +} + +std::string Utils::GetPathBase( std::string path ) +{ + size_t lastIndex = path.find_last_of("."); + if (lastIndex != std::string::npos) + return path.substr(0, lastIndex); + return path; +} + +Vector3 Vector3::Read( FILE* file ) +{ + Vector3 ret; + if (fread(&ret, sizeof(Vector3), 1, file) != 1) + printf("Vector3::Read: Failed to read some data expected 1, read 0\n"); + return ret; +} + +Vector3 Utils::GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int /*x*/, int /*y*/) +{ + if (Utils::Distance(height, 0.0f) > 0.5f) + basePosition.z = 0.0f; + return Utils::VectorTransform(basePosition + Vector3(basePosition.x * Constants::UnitSize, basePosition.y * Constants::UnitSize, height), transformation); +} + +float Utils::Distance( float x, float y ) +{ + return sqrt(x*x + y*y); +} + +std::string Utils::Replace( std::string str, const std::string& oldStr, const std::string& newStr ) +{ + size_t pos = 0; + while((pos = str.find(oldStr, pos)) != std::string::npos) + { + str.replace(pos, oldStr.length(), newStr); + pos += newStr.length(); + } + return str; +} + +G3D::Matrix4 Utils::GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root ) +{ + G3D::Matrix4 rootTransformation = Utils::GetTransformation(root); + G3D::Matrix4 translation = G3D::Matrix4::translation(inst.Position.x, inst.Position.y, inst.Position.z); + G3D::Matrix4 scale = G3D::Matrix4::scale(inst.Scale); + G3D::Matrix4 rotation = Utils::RotationY(Constants::PI); + G3D::Quat quat(-inst.QuatY, inst.QuatZ, -inst.QuatX, inst.QuatW); + G3D::Matrix4 quatRotation = quat.toRotationMatrix(); + + return scale * rotation * quatRotation ** translation * rootTransformation; +} + +void Utils::SaveToDisk( FILE* stream, std::string path ) +{ + FILE* disk = fopen(path.c_str(), "wb"); + if (!disk) + { + printf("SaveToDisk: Could not save file %s to disk, please verify that you have write permissions on that directory\n", path.c_str()); + return; + } + + uint32 size = Utils::Size(stream); + uint8* data = new uint8[size]; + // Read the data to an array + if (fread(data, 1, size, stream) != 1) + { + printf("SaveToDisk: Error reading from Stream while trying to save file %s to disck.\n", path.c_str()); + return; + } + // And write it in the file + fwrite(data, 1, size, disk); + + // Close the filestream + fclose(disk); + // Free the used memory + delete data; +} + +Vector3 Utils::ToWoWCoords( Vector3 vec ) +{ + return Vector3(vec.x, -vec.z, vec.y); +} + +std::string Utils::GetExtension( std::string path ) +{ + std::string::size_type idx = path.rfind('.'); + std::string extension = ""; + + if(idx != std::string::npos) + extension = path.substr(idx+1); + return extension; +} + +void MapChunkHeader::Read(FILE* stream) +{ + int count = 0; + + count += fread(&Flags, sizeof(uint32), 1, stream); + count += fread(&IndexX, sizeof(uint32), 1, stream); + count += fread(&IndexY, sizeof(uint32), 1, stream); + count += fread(&Layers, sizeof(uint32), 1, stream); + count += fread(&DoodadRefs, sizeof(uint32), 1, stream); + count += fread(&OffsetMCVT, sizeof(uint32), 1, stream); + count += fread(&OffsetMCNR, sizeof(uint32), 1, stream); + count += fread(&OffsetMCLY, sizeof(uint32), 1, stream); + count += fread(&OffsetMCRF, sizeof(uint32), 1, stream); + count += fread(&OffsetMCAL, sizeof(uint32), 1, stream); + count += fread(&SizeMCAL, sizeof(uint32), 1, stream); + count += fread(&OffsetMCSH, sizeof(uint32), 1, stream); + count += fread(&SizeMCSH, sizeof(uint32), 1, stream); + count += fread(&AreaId, sizeof(uint32), 1, stream); + count += fread(&MapObjectRefs, sizeof(uint32), 1, stream); + count += fread(&Holes, sizeof(uint32), 1, stream); + LowQualityTextureMap = new uint32[4]; + count += fread(LowQualityTextureMap, sizeof(uint32), 4, stream); + count += fread(&PredTex, sizeof(uint32), 1, stream); + count += fread(&NumberEffectDoodad, sizeof(uint32), 1, stream); + count += fread(&OffsetMCSE, sizeof(uint32), 1, stream); + count += fread(&SoundEmitters, sizeof(uint32), 1, stream); + count += fread(&OffsetMCLQ, sizeof(uint32), 1, stream); + count += fread(&SizeMCLQ, sizeof(uint32), 1, stream); + Position = Vector3::Read(stream); + count += fread(&OffsetMCCV, sizeof(uint32), 1, stream); + + if (count != 27) + printf("MapChunkHeader::Read: Failed to read some data expected 27, read %d\n", count); +} + +void MHDR::Read(FILE* stream) +{ + int count = 0; + + count += fread(&Flags, sizeof(uint32), 1, stream); + count += fread(&OffsetMCIN, sizeof(uint32), 1, stream); + count += fread(&OffsetMTEX, sizeof(uint32), 1, stream); + count += fread(&OffsetMMDX, sizeof(uint32), 1, stream); + count += fread(&OffsetMMID, sizeof(uint32), 1, stream); + count += fread(&OffsetMWMO, sizeof(uint32), 1, stream); + count += fread(&OffsetMWID, sizeof(uint32), 1, stream); + count += fread(&OffsetMDDF, sizeof(uint32), 1, stream); + count += fread(&OffsetMODF, sizeof(uint32), 1, stream); + count += fread(&OffsetMFBO, sizeof(uint32), 1, stream); + count += fread(&OffsetMH2O, sizeof(uint32), 1, stream); + count += fread(&OffsetMTFX, sizeof(uint32), 1, stream); + + if (count != 12) + printf("MHDR::Read: Failed to read some data expected 12, read %d\n", count); +} + +void ModelHeader::Read(FILE* stream) +{ + int count = 0; + + count += fread(&Magic, sizeof(char), 4, stream); + Magic[4] = '\0'; // null-terminate it. + count += fread(&Version, sizeof(uint32), 1, stream); + count += fread(&LengthModelName, sizeof(uint32), 1, stream); + count += fread(&OffsetName, sizeof(uint32), 1, stream); + count += fread(&ModelFlags, sizeof(uint32), 1, stream); + count += fread(&CountGlobalSequences, sizeof(uint32), 1, stream); + count += fread(&OffsetGlobalSequences, sizeof(uint32), 1, stream); + count += fread(&CountAnimations, sizeof(uint32), 1, stream); + count += fread(&OffsetAnimations, sizeof(uint32), 1, stream); + count += fread(&CountAnimationLookup, sizeof(uint32), 1, stream); + count += fread(&OffsetAnimationLookup, sizeof(uint32), 1, stream); + count += fread(&CountBones, sizeof(uint32), 1, stream); + count += fread(&OffsetBones, sizeof(uint32), 1, stream); + count += fread(&CountKeyBoneLookup, sizeof(uint32), 1, stream); + count += fread(&OffsetKeyBoneLookup, sizeof(uint32), 1, stream); + count += fread(&CountVertices, sizeof(uint32), 1, stream); + count += fread(&OffsetVertices, sizeof(uint32), 1, stream); + count += fread(&CountViews, sizeof(uint32), 1, stream); + count += fread(&CountColors, sizeof(uint32), 1, stream); + count += fread(&OffsetColors, sizeof(uint32), 1, stream); + count += fread(&CountTextures, sizeof(uint32), 1, stream); + count += fread(&OffsetTextures, sizeof(uint32), 1, stream); + count += fread(&CountTransparency, sizeof(uint32), 1, stream); + count += fread(&OffsetTransparency, sizeof(uint32), 1, stream); + count += fread(&CountUvAnimation, sizeof(uint32), 1, stream); + count += fread(&OffsetUvAnimation, sizeof(uint32), 1, stream); + count += fread(&CountTexReplace, sizeof(uint32), 1, stream); + count += fread(&OffsetTexReplace, sizeof(uint32), 1, stream); + count += fread(&CountRenderFlags, sizeof(uint32), 1, stream); + count += fread(&OffsetRenderFlags, sizeof(uint32), 1, stream); + count += fread(&CountBoneLookup, sizeof(uint32), 1, stream); + count += fread(&OffsetBoneLookup, sizeof(uint32), 1, stream); + count += fread(&CountTexLookup, sizeof(uint32), 1, stream); + count += fread(&OffsetTexLookup, sizeof(uint32), 1, stream); + count += fread(&CountTexUnits, sizeof(uint32), 1, stream); + count += fread(&OffsetTexUnits, sizeof(uint32), 1, stream); + count += fread(&CountTransLookup, sizeof(uint32), 1, stream); + count += fread(&OffsetTransLookup, sizeof(uint32), 1, stream); + count += fread(&CountUvAnimLookup, sizeof(uint32), 1, stream); + count += fread(&OffsetUvAnimLookup, sizeof(uint32), 1, stream); + VertexBox[0] = Vector3::Read(stream); + VertexBox[1] = Vector3::Read(stream); + count += fread(&VertexRadius, sizeof(float), 1, stream); + BoundingBox[0] = Vector3::Read(stream); + BoundingBox[1] = Vector3::Read(stream); + count += fread(&BoundingRadius, sizeof(float), 1, stream); + count += fread(&CountBoundingTriangles, sizeof(uint32), 1, stream); + count += fread(&OffsetBoundingTriangles, sizeof(uint32), 1, stream); + count += fread(&CountBoundingVertices, sizeof(uint32), 1, stream); + count += fread(&OffsetBoundingVertices, sizeof(uint32), 1, stream); + count += fread(&CountBoundingNormals, sizeof(uint32), 1, stream); + count += fread(&OffsetBoundingNormals, sizeof(uint32), 1, stream); + + if (count != 51) + printf("ModelHeader::Read: Failed to read some data expected 51, read %d\n", count); + +} + +WorldModelHeader WorldModelHeader::Read(FILE* stream) +{ + WorldModelHeader ret; + int count = 0; + + count += fread(&ret.CountMaterials, sizeof(uint32), 1, stream); + count += fread(&ret.CountGroups, sizeof(uint32), 1, stream); + count += fread(&ret.CountPortals, sizeof(uint32), 1, stream); + count += fread(&ret.CountLights, sizeof(uint32), 1, stream); + count += fread(&ret.CountModels, sizeof(uint32), 1, stream); + count += fread(&ret.CountDoodads, sizeof(uint32), 1, stream); + count += fread(&ret.CountSets, sizeof(uint32), 1, stream); + count += fread(&ret.AmbientColorUnk, sizeof(uint32), 1, stream); + count += fread(&ret.WmoId, sizeof(uint32), 1, stream); + ret.BoundingBox[0] = Vector3::Read(stream); + ret.BoundingBox[1] = Vector3::Read(stream); + count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream); + + if (count != 10) + printf("WorldModelHeader::Read: Failed to read some data expected 10, read %d\n", count); + + return ret; +} + +DoodadInstance DoodadInstance::Read(FILE* stream) +{ + DoodadInstance ret; + int count = 0; + + count += fread(&ret.FileOffset, sizeof(uint32), 1, stream); + ret.Position = Vector3::Read(stream); + count += fread(&ret.QuatW, sizeof(float), 1, stream); + count += fread(&ret.QuatX, sizeof(float), 1, stream); + count += fread(&ret.QuatY, sizeof(float), 1, stream); + count += fread(&ret.QuatZ, sizeof(float), 1, stream); + count += fread(&ret.Scale, sizeof(float), 1, stream); + count += fread(&ret.LightColor, sizeof(uint32), 1, stream); + + if (count != 7) + printf("DoodadInstance::Read: Failed to read some data expected 7, read %d\n", count); + + return ret; +} + +DoodadSet DoodadSet::Read(FILE* stream) +{ + DoodadSet ret; + char name[21]; + int count = 0; + + count += fread(&name, sizeof(char), 20, stream); + name[20] = '\0'; + ret.Name = name; + count += fread(&ret.FirstInstanceIndex, sizeof(uint32), 1, stream); + count += fread(&ret.CountInstances, sizeof(uint32), 1, stream); + count += fread(&ret.UnknownZero, sizeof(uint32), 1, stream); + + if (count != 23) + printf("DoodadSet::Read: Failed to read some data expected 23, read %d\n", count); + + return ret; +} + +LiquidHeader LiquidHeader::Read(FILE* stream) +{ + LiquidHeader ret; + int count = 0; + count += fread(&ret.CountXVertices, sizeof(uint32), 1, stream); + count += fread(&ret.CountYVertices, sizeof(uint32), 1, stream); + count += fread(&ret.Width, sizeof(uint32), 1, stream); + count += fread(&ret.Height, sizeof(uint32), 1, stream); + ret.BaseLocation = Vector3::Read(stream); + count += fread(&ret.MaterialId, sizeof(uint16), 1, stream); + + if (count != 5) + printf("LiquidHeader::Read: Failed to read some data expected 5, read %d\n", count); + + return ret; +} + +LiquidData LiquidData::Read(FILE* stream, LiquidHeader& header) +{ + LiquidData ret; + ret.HeightMap = new float*[header.CountXVertices]; + for (uint32 i = 0; i < header.CountXVertices; ++i) + ret.HeightMap[i] = new float[header.CountYVertices]; + + ret.RenderFlags = new uint8*[header.Width]; + for (uint32 i = 0; i < header.Width; ++i) + ret.RenderFlags[i] = new uint8[header.Height]; + + for (uint32 y = 0; y < header.CountYVertices; y++) + { + for (uint32 x = 0; x < header.CountXVertices; x++) + { + uint32 discard; + float tmp; + if (fread(&discard, sizeof(uint32), 1, stream) == 1 && + fread(&tmp, sizeof(float), 1, stream) == 1) + { + ret.HeightMap[x][y] = tmp; + } + } + } + + for (uint32 y = 0; y < header.Height; y++) + { + for (uint32 x = 0; x < header.Width; x++) + { + uint8 tmp = 0; + if (fread(&tmp, sizeof(uint8), 1, stream) == 1) + ret.RenderFlags[x][y] = tmp; + } + } + + return ret; +} + +H2ORenderMask H2ORenderMask::Read(FILE* stream) +{ + H2ORenderMask ret; + if (int count = fread(&ret.Mask, sizeof(uint8), 8, stream) != 8) + printf("H2OHeader::Read: Failed to read some data expected 8, read %d\n", count); + return ret; +} + +bool MCNKLiquidData::IsWater(int x, int y, float height) +{ + if (!Heights) + return false; + if (!Mask.ShouldRender(x, y)) + return false; + float diff = Heights[x][y] - height; + if (diff > Constants::MaxStandableHeight) + return true; + return false; +} + +H2OHeader H2OHeader::Read(FILE* stream) +{ + H2OHeader ret; + int count = 0; + count += fread(&ret.OffsetInformation, sizeof(uint32), 1, stream); + count += fread(&ret.LayerCount, sizeof(uint32), 1, stream); + count += fread(&ret.OffsetRender, sizeof(uint32), 1, stream); + + if (count != 3) + printf("H2OHeader::Read: Failed to read some data expected 3, read %d\n", count); + + return ret; +} + +H2OInformation H2OInformation::Read(FILE* stream) +{ + H2OInformation ret; + int count = 0; + count += fread(&ret.LiquidType, sizeof(uint16), 1, stream); + count += fread(&ret.Flags, sizeof(uint16), 1, stream); + count += fread(&ret.HeightLevel1, sizeof(float), 1, stream); + count += fread(&ret.HeightLevel2, sizeof(float), 1, stream); + count += fread(&ret.OffsetX, sizeof(uint8), 1, stream); + count += fread(&ret.OffsetY, sizeof(uint8), 1, stream); + count += fread(&ret.Width, sizeof(uint8), 1, stream); + count += fread(&ret.Height, sizeof(uint8), 1, stream); + count += fread(&ret.OffsetMask2, sizeof(uint32), 1, stream); + count += fread(&ret.OffsetHeightmap, sizeof(uint32), 1, stream); + + if (count != 10) + printf("H2OInformation::Read: Failed to read some data expected 10, read %d\n", count); + + return ret; +} + +char* Utils::GetPlainName(const char* FileName) +{ + char* temp; + + if((temp = (char*)strrchr(FileName, '\\')) != NULL) + FileName = temp + 1; + return (char*)FileName; +} + +WMOGroupHeader WMOGroupHeader::Read( FILE* stream ) +{ + WMOGroupHeader ret; + int count = 0; + count += fread(&ret.OffsetGroupName, sizeof(uint32), 1, stream); + count += fread(&ret.OffsetDescriptiveName, sizeof(uint32), 1, stream); + count += fread(&ret.Flags, sizeof(uint32), 1, stream); + ret.BoundingBox[0] = Vector3::Read(stream); + ret.BoundingBox[1] = Vector3::Read(stream); + count += fread(&ret.OffsetPortals, sizeof(uint32), 1, stream); + count += fread(&ret.CountPortals, sizeof(uint32), 1, stream); + count += fread(&ret.CountBatches, sizeof(uint16), 4, stream); + count += fread(&ret.Fogs, sizeof(uint8), 4, stream); + count += fread(&ret.LiquidTypeRelated, sizeof(uint32), 1, stream); + count += fread(&ret.WmoId, sizeof(uint32), 1, stream); + + if (count != 15) + printf("WMOGroupHeader::Read: Failed to read some data expected 15, read %d\n", count); + + return ret; +} diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h new file mode 100644 index 00000000000..64fb1bb35ba --- /dev/null +++ b/src/tools/mesh_extractor/Utils.h @@ -0,0 +1,381 @@ +#ifndef UTILS_H +#define UTILS_H +#include <cstdio> +#include <string> +#include <sstream> + +#include "G3D/Matrix4.h" +#include "DetourNavMesh.h" + +#include "Define.h" +#include "Constants.h" + +#include <ace/Stack_Trace.h> + +struct WorldModelDefinition; +class DoodadInstance; + +#define ASSERT(assertion) { if (!(assertion)) { ACE_Stack_Trace st; fprintf(stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__, __FUNCTION__, #assertion, st.c_str()); *((volatile int*)NULL) = 0; } } + +struct Vector3 +{ + Vector3() {} + Vector3(float X, float Y, float Z) : x(X), y(Y), z(Z) {} + float x; + float y; + float z; + + Vector3 operator +(Vector3 const& other) + { + return Vector3(x + other.x, y + other.y, z + other.z); + } + + static Vector3 Read(FILE* file); +}; + +struct TilePos +{ + TilePos(int x, int y) : X(x), Y(y) {} + int X; + int Y; +}; + +template<typename T> +struct Triangle +{ + Triangle() {} + Triangle(Constants::TriangleType type, T v0, T v1, T v2) : V0(v0), V1(v1), V2(v2), Type(type) {} + T V0; + T V1; + T V2; + Constants::TriangleType Type; +}; + +class MapChunkHeader +{ +public: + MapChunkHeader() {} + uint32 Flags; + uint32 IndexX; + uint32 IndexY; + uint32 Layers; + uint32 DoodadRefs; + uint32 OffsetMCVT; + uint32 OffsetMCNR; + uint32 OffsetMCLY; + uint32 OffsetMCRF; + uint32 OffsetMCAL; + uint32 SizeMCAL; + uint32 OffsetMCSH; + uint32 SizeMCSH; + uint32 AreaId; + uint32 MapObjectRefs; + uint32 Holes; + uint32* LowQualityTextureMap; + uint32 PredTex; + uint32 NumberEffectDoodad; + uint32 OffsetMCSE; + uint32 SoundEmitters; + uint32 OffsetMCLQ; + uint32 SizeMCLQ; + Vector3 Position; + uint32 OffsetMCCV; + + void Read(FILE* stream); +}; + +class MHDR +{ +public: + MHDR() {} + uint32 Flags; + uint32 OffsetMCIN; + uint32 OffsetMTEX; + uint32 OffsetMMDX; + uint32 OffsetMMID; + uint32 OffsetMWMO; + uint32 OffsetMWID; + uint32 OffsetMDDF; + uint32 OffsetMODF; + uint32 OffsetMFBO; + uint32 OffsetMH2O; + uint32 OffsetMTFX; + + void Read(FILE* stream); +}; + +class ModelHeader +{ +public: + char Magic[5]; + uint32 Version; + uint32 LengthModelName; + uint32 OffsetName; + uint32 ModelFlags; + uint32 CountGlobalSequences; + uint32 OffsetGlobalSequences; + uint32 CountAnimations; + uint32 OffsetAnimations; + uint32 CountAnimationLookup; + uint32 OffsetAnimationLookup; + uint32 CountBones; + uint32 OffsetBones; + uint32 CountKeyBoneLookup; + uint32 OffsetKeyBoneLookup; + uint32 CountVertices; + uint32 OffsetVertices; + uint32 CountViews; + uint32 CountColors; + uint32 OffsetColors; + uint32 CountTextures; + uint32 OffsetTextures; + uint32 CountTransparency; + uint32 OffsetTransparency; + uint32 CountUvAnimation; + uint32 OffsetUvAnimation; + uint32 CountTexReplace; + uint32 OffsetTexReplace; + uint32 CountRenderFlags; + uint32 OffsetRenderFlags; + uint32 CountBoneLookup; + uint32 OffsetBoneLookup; + uint32 CountTexLookup; + uint32 OffsetTexLookup; + uint32 CountTexUnits; + uint32 OffsetTexUnits; + uint32 CountTransLookup; + uint32 OffsetTransLookup; + uint32 CountUvAnimLookup; + uint32 OffsetUvAnimLookup; + Vector3 VertexBox[2]; + float VertexRadius; + Vector3 BoundingBox[2]; + float BoundingRadius; + uint32 CountBoundingTriangles; + uint32 OffsetBoundingTriangles; + uint32 CountBoundingVertices; + uint32 OffsetBoundingVertices; + uint32 CountBoundingNormals; + uint32 OffsetBoundingNormals; + + void Read(FILE* stream); +}; + +class WorldModelHeader +{ +public: + WorldModelHeader() {} + uint32 CountMaterials; + uint32 CountGroups; + uint32 CountPortals; + uint32 CountLights; + uint32 CountModels; + uint32 CountDoodads; + uint32 CountSets; + uint32 AmbientColorUnk; + uint32 WmoId; + Vector3 BoundingBox[2]; + uint32 LiquidTypeRelated; + + static WorldModelHeader Read(FILE* stream); +}; + +class DoodadInstance +{ +public: + DoodadInstance() {} + uint32 FileOffset; + std::string File; + Vector3 Position; + float QuatW; + float QuatX; + float QuatY; + float QuatZ; + float Scale; + uint32 LightColor; + + static DoodadInstance Read(FILE* stream); +}; + +class DoodadSet +{ +public: + DoodadSet() {} + std::string Name; + uint32 FirstInstanceIndex; + uint32 CountInstances; + uint32 UnknownZero; + + static DoodadSet Read(FILE* stream); +}; + +class LiquidHeader +{ +public: + LiquidHeader() {} + uint32 CountXVertices; + uint32 CountYVertices; + uint32 Width; + uint32 Height; + Vector3 BaseLocation; + uint16 MaterialId; + + static LiquidHeader Read(FILE* stream); +}; + +class LiquidData +{ +public: + LiquidData() {} + float** HeightMap; + uint8** RenderFlags; + + bool ShouldRender(int x, int y) + { + return RenderFlags[x][y] != 0x0F; + } + + static LiquidData Read(FILE* stream, LiquidHeader& header); +}; + +class H2ORenderMask +{ +public: + H2ORenderMask() {} + uint8 Mask[8]; + + bool ShouldRender(int x, int y) + { + return (Mask[y] >> x & 1) != 0; + } + + static H2ORenderMask Read(FILE* stream); +}; + +class MCNKLiquidData +{ +public: + MCNKLiquidData() {} + MCNKLiquidData(float** heights, H2ORenderMask mask) : Heights(heights), Mask(mask) {} + + float** Heights; + H2ORenderMask Mask; + + bool IsWater(int x, int y, float height); +}; + +class H2OHeader +{ +public: + H2OHeader() {} + uint32 OffsetInformation; + uint32 LayerCount; + uint32 OffsetRender; + + static H2OHeader Read(FILE* stream); +}; + +class H2OInformation +{ +public: + H2OInformation() {} + uint16 LiquidType; + uint16 Flags; + float HeightLevel1; + float HeightLevel2; + uint8 OffsetX; + uint8 OffsetY; + uint8 Width; + uint8 Height; + uint32 OffsetMask2; + uint32 OffsetHeightmap; + + static H2OInformation Read(FILE* stream); +}; + +class WMOGroupHeader +{ +public: + WMOGroupHeader() {} + + uint32 OffsetGroupName; + uint32 OffsetDescriptiveName; + uint32 Flags; + Vector3 BoundingBox[2]; + uint32 OffsetPortals; + uint32 CountPortals; + uint16 CountBatches[4]; + uint8 Fogs[4]; + uint32 LiquidTypeRelated; + uint32 WmoId; + + static WMOGroupHeader Read(FILE* stream); +}; + +// Dummy class to act as an interface. +class IDefinition +{ +public: + Vector3 Position; + Vector3 Rotation; + virtual float Scale() const { return 1.0f; }; +}; + +#define MMAP_MAGIC 0x4d4d4150 // 'MMAP' +#define MMAP_VERSION 3 + +struct MmapTileHeader +{ + uint32 mmapMagic; + uint32 dtVersion; + uint32 mmapVersion; + uint32 size; + bool usesLiquids; + + MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION), + mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {} +}; + +class Utils +{ +public: + static void Reverse(char word[]); + static std::string ReadString(FILE* file); + static uint32 Size(FILE* file); + static Vector3 ToRecast( Vector3 val ); + static std::string GetAdtPath(std::string world, int x, int y); + static std::string FixModelPath(std::string path); + static G3D::Matrix4 GetTransformation(IDefinition def); + /// They say its better to declare template functions in the header files. + template <typename T> + static std::string ToString(T val) + { + std::stringstream ss; + ss << val; + return ss.str(); + } + static G3D::Matrix4 RotationX(float angle); + static G3D::Matrix4 RotationY(float angle); + static G3D::Matrix4 RotationZ(float angle); + static float ToRadians(float degrees); + static Vector3 VectorTransform(Vector3 vec, G3D::Matrix4 matrix); + static std::string GetPathBase(std::string path); + static Vector3 GetLiquidVert(G3D::Matrix4 transformation, Vector3 basePosition, float height, int x, int y); + static float Distance(float x, float y); + template<typename T> + static bool IsAllZero(T* arr, uint32 size) + { + for (uint32 i = 0; i < size; ++i) + if (arr[i]) + return false; + return true; + } + static std::string Replace( std::string str, const std::string& oldStr, const std::string& newStr ); + static G3D::Matrix4 GetWmoDoodadTransformation( DoodadInstance inst, WorldModelDefinition root ); + static void CreateDir( const std::string& Path ); + static void SaveToDisk(FILE* stream, std::string path); + static Vector3 ToWoWCoords( Vector3 vec ); + static std::string GetExtension( std::string path ); + static char* GetPlainName(const char* FileName); +}; +#endif diff --git a/src/tools/mesh_extractor/WDT.cpp b/src/tools/mesh_extractor/WDT.cpp new file mode 100644 index 00000000000..70d140e79ed --- /dev/null +++ b/src/tools/mesh_extractor/WDT.cpp @@ -0,0 +1,60 @@ +#include "WDT.h" +#include "Chunk.h" +#include "ChunkedData.h" +#include "Utils.h" +#include "WorldModelHandler.h" + +WDT::WDT(std::string file) : IsGlobalModel(false), IsValid(false) +{ + Data = new ChunkedData(file, 2); + ReadTileTable(); + ReadGlobalModel(); +} + +void WDT::ReadGlobalModel() +{ + Chunk* fileChunk = Data->GetChunkByName("MWMO"); + Chunk* defChunk = Data->GetChunkByName("MODF"); + if (!fileChunk || !defChunk) + return; + + IsGlobalModel = true; + ModelDefinition = WorldModelDefinition::Read(defChunk->GetStream()); + ModelFile = Utils::ReadString(fileChunk->GetStream()); +} + +void WDT::ReadTileTable() +{ + Chunk* chunk = Data->GetChunkByName("MAIN"); + if (!chunk) + return; + IsValid = true; + FILE* stream = chunk->GetStream(); + for (int y = 0; y < 64; ++y) + { + for (int x = 0; x < 64; ++x) + { + const uint32 hasTileFlag = 0x1; + uint32 flags; + uint32 discard; + int count = 0; + count += fread(&flags, sizeof(uint32), 1, stream); + count += fread(&discard, sizeof(uint32), 1, stream); + + if (count != 2) + printf("WDT::ReadTileTable: Failed to read some data expected 2, read %d\n", count); + + if (flags & hasTileFlag) + TileTable.push_back(TilePos(x, y)); + + } + } +} + +bool WDT::HasTile( int x, int y ) +{ + for (std::vector<TilePos>::iterator itr = TileTable.begin(); itr != TileTable.end(); ++itr) + if (itr->X == x && itr->Y == y) + return true; + return false; +} diff --git a/src/tools/mesh_extractor/WDT.h b/src/tools/mesh_extractor/WDT.h new file mode 100644 index 00000000000..a12aa65218b --- /dev/null +++ b/src/tools/mesh_extractor/WDT.h @@ -0,0 +1,27 @@ +#ifndef WDT_H +#define WDT_H +#include <string> +#include <vector> + +#include "ChunkedData.h" +#include "WorldModelHandler.h" +#include "Utils.h" + +class WDT +{ +public: + WDT(std::string file); + + ChunkedData* Data; + std::vector<TilePos> TileTable; + bool IsGlobalModel; + bool IsValid; + std::string ModelFile; + WorldModelDefinition ModelDefinition; + bool HasTile(int x, int y); +private: + void ReadGlobalModel(); + void ReadTileTable(); +}; + +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/WorldModelGroup.cpp b/src/tools/mesh_extractor/WorldModelGroup.cpp new file mode 100644 index 00000000000..21e1c1e63e1 --- /dev/null +++ b/src/tools/mesh_extractor/WorldModelGroup.cpp @@ -0,0 +1,143 @@ +#include "WorldModelGroup.h" +#include "ChunkedData.h" +#include "Chunk.h" +#include "Utils.h" + +WorldModelGroup::WorldModelGroup( std::string path, int groupIndex ) : GroupIndex(groupIndex), MOBA(NULL), IsBad(false) +{ + Data = new ChunkedData(path); + if (!Data->Stream) + { + IsBad = true; + return; + } + Chunk* mainChunk = Data->GetChunkByName("MOGP"); + int32 firstSub = mainChunk->FindSubChunkOffset("MOPY"); + if (firstSub == -1) + return; + + Name = Utils::GetPlainName(path.c_str()); + + FILE* stream = mainChunk->GetStream(); + fseek(stream, firstSub, SEEK_SET); + SubData = new ChunkedData(stream, mainChunk->Length - firstSub); + + ReadHeader(); + ReadMaterials(); + ReadTriangles(); + ReadVertices(); + ReadNormals(); + ReadLiquid(); + ReadBatches(); +} + +void WorldModelGroup::ReadNormals() +{ + Chunk* chunk = SubData->GetChunkByName("MONR"); + if (!chunk) + return; + + uint32 normalCount = chunk->Length / 12; + ASSERT(normalCount == Vertices.size() && "normalCount is different than the Vertices count"); + Normals.reserve(normalCount); + FILE* stream = chunk->GetStream(); + for (uint32 i = 0; i < normalCount; i++) + Normals.push_back(Vector3::Read(stream)); +} + +void WorldModelGroup::ReadLiquid() +{ + Chunk* chunk = SubData->GetChunkByName("MLIQ"); + if (!chunk) + return; + + HasLiquidData = true; + FILE* stream = chunk->GetStream(); + LiquidDataHeader = LiquidHeader::Read(stream); + LiquidDataGeometry = LiquidData::Read(stream, LiquidDataHeader); +} + +void WorldModelGroup::ReadVertices() +{ + Chunk* chunk = SubData->GetChunkByName("MOVT"); + if (!chunk) + return; + + uint32 verticeCount = chunk->Length / 12; + Vertices.reserve(verticeCount); + FILE* stream = chunk->GetStream(); + for (uint32 i = 0; i < verticeCount; i++) + Vertices.push_back(Vector3::Read(stream)); +} + +void WorldModelGroup::ReadTriangles() +{ + Chunk* chunk = SubData->GetChunkByName("MOVI"); + if (!chunk) + return; + + uint32 triangleCount = chunk->Length / 6; + ASSERT(triangleCount == TriangleFlags.size() && "triangleCount != TriangleFlags.size()"); + FILE* stream = chunk->GetStream(); + Triangles.reserve(triangleCount); + for (uint32 i = 0; i < triangleCount; i++) + { + uint16 v0; + uint16 v1; + uint16 v2; + int count = 0; + count += fread(&v0, sizeof(uint16), 1, stream); + count += fread(&v1, sizeof(uint16), 1, stream); + count += fread(&v2, sizeof(uint16), 1, stream); + if (count != 3) + printf("WorldModelGroup::ReadMaterials: Error reading data, expected 3, read %d\n", count); + + Triangles.push_back(Triangle<uint16>(Constants::TRIANGLE_TYPE_WMO, v0, v1, v2)); + } +} + +void WorldModelGroup::ReadMaterials() +{ + Chunk* chunk = SubData->GetChunkByName("MOPY"); + if (!chunk) + return; + + FILE* stream = chunk->GetStream(); + uint32 triangleCount = chunk->Length / 2; + TriangleFlags.reserve(triangleCount); + TriangleMaterials.reserve(triangleCount); + for (uint32 i = 0; i < triangleCount; i++) + { + uint8 tmp; + if (fread(&tmp, sizeof(uint8), 1, stream) != 1) + printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n"); + TriangleFlags.push_back(tmp); + // Read again for material. + if (fread(&tmp, sizeof(uint8), 1, stream) != 1) + printf("WorldModelGroup::ReadMaterials: Error reading data, expected 1, read 0\n"); + TriangleMaterials.push_back(tmp); + } +} + +void WorldModelGroup::ReadHeader() +{ + Chunk* chunk = Data->GetChunkByName("MOGP"); + if (!chunk) + return; + + FILE* stream = chunk->GetStream(); + Header = WMOGroupHeader::Read(stream); +} + +void WorldModelGroup::ReadBatches() +{ + Chunk* chunk = Data->GetChunkByName("MOBA"); + if (!chunk) + return; + + MOBALength = chunk->Length / 2; + MOBA = new uint16[MOBALength]; + uint32 count = (uint32)fread(MOBA, sizeof(uint16), MOBALength, chunk->GetStream()); + if (count != MOBALength) + printf("WorldModelGroup::ReadBatches: Error reading data, expected %u, read %u\n", MOBALength, count); +} diff --git a/src/tools/mesh_extractor/WorldModelGroup.h b/src/tools/mesh_extractor/WorldModelGroup.h new file mode 100644 index 00000000000..e4fe34cbc75 --- /dev/null +++ b/src/tools/mesh_extractor/WorldModelGroup.h @@ -0,0 +1,38 @@ +#ifndef WMOGROUP_H +#define WMOGROUP_H +#include "ChunkedData.h" +#include "Utils.h" + +class WorldModelGroup +{ +public: + WorldModelGroup(std::string path, int groupIndex); + ChunkedData* Data; + ChunkedData* SubData; + int GroupIndex; + std::string Name; + WMOGroupHeader Header; + + std::vector<uint8> TriangleFlags; + std::vector<uint8> TriangleMaterials; + std::vector<Triangle<uint16> > Triangles; + std::vector<Vector3> Vertices; + std::vector<Vector3> Normals; + // @ToDo: Research. + uint16* MOBA; + uint32 MOBALength; + + bool HasLiquidData; + bool IsBad; + LiquidHeader LiquidDataHeader; + LiquidData LiquidDataGeometry; +private: + void ReadNormals(); + void ReadLiquid(); + void ReadVertices(); + void ReadTriangles(); + void ReadMaterials(); + void ReadHeader(); + void ReadBatches(); +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/WorldModelHandler.cpp b/src/tools/mesh_extractor/WorldModelHandler.cpp new file mode 100644 index 00000000000..ecfff4e97d4 --- /dev/null +++ b/src/tools/mesh_extractor/WorldModelHandler.cpp @@ -0,0 +1,204 @@ +#include "WorldModelHandler.h" +#include "WorldModelRoot.h" +#include "Chunk.h" +#include "Cache.h" +#include "Model.h" +#include "Define.h" +#include "G3D/Matrix4.h" +#include <cstdio> + +WorldModelDefinition WorldModelDefinition::Read( FILE* file ) +{ + WorldModelDefinition ret; + int count = 0; + count += fread(&ret.MwidIndex, sizeof(uint32), 1, file); + count += fread(&ret.UniqueId, sizeof(uint32), 1, file); + ret.Position = Vector3::Read(file); + ret.Rotation = Vector3::Read(file); + ret.UpperExtents = Vector3::Read(file); + ret.LowerExtents = Vector3::Read(file); + count += fread(&ret.Flags, sizeof(uint16), 1, file); + count += fread(&ret.DoodadSet, sizeof(uint16), 1, file); + uint32 discard; + count += fread(&discard, sizeof(uint32), 1, file); + + if (count != 5) + printf("WorldModelDefinition::Read: Error reading data, expected 5, read %d\n", count); + return ret; +} + + +WorldModelHandler::WorldModelHandler( ADT* adt ) : ObjectDataHandler(adt), _definitions(NULL), _paths(NULL) +{ + if (!adt->HasObjectData) + return; + ReadModelPaths(); + ReadDefinitions(); +} + +void WorldModelHandler::ProcessInternal( ChunkedData* subChunks ) +{ + if (!IsSane()) + return; + Chunk* wmoReferencesChunk = subChunks->GetChunkByName("MCRW"); + if (!wmoReferencesChunk) + return; + FILE* stream = wmoReferencesChunk->GetStream(); + uint32 refCount = wmoReferencesChunk->Length / 4; + for (uint32 i = 0; i < refCount; i++) + { + int32 index; + if (fread(&index, sizeof(int32), 1, stream) != 1) + printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n"); + + if (index < 0 || uint32(index) >= _definitions->size()) + continue; + + WorldModelDefinition wmo = (*_definitions)[index]; + + if (_drawn.find(wmo.UniqueId) != _drawn.end()) + continue; + _drawn.insert(wmo.UniqueId); + + if (wmo.MwidIndex >= _paths->size()) + continue; + + std::string path = (*_paths)[wmo.MwidIndex]; + WorldModelRoot* model = Cache->WorldModelCache.Get(path); + if (!model) + { + model = new WorldModelRoot(path); + Cache->WorldModelCache.Insert(path, model); + } + + Vertices.reserve(1000); + Triangles.reserve(1000); + + InsertModelGeometry(Vertices, Triangles, wmo, model); + } +} + +void WorldModelHandler::InsertModelGeometry( std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root ) +{ + G3D::Matrix4 transformation = Utils::GetTransformation(def); + for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group) + { + uint32 vertOffset = verts.size(); + for (std::vector<Vector3>::iterator itr2 = group->Vertices.begin(); itr2 != group->Vertices.end(); ++itr2) + verts.push_back(Utils::VectorTransform(*itr2, transformation)); + + for (uint32 i = 0; i < group->Triangles.size(); ++i) + { + // only include collidable tris + if ((group->TriangleFlags[i] & 0x04) != 0 && group->TriangleMaterials[i] != 0xFF) + continue; + Triangle<uint16> tri = group->Triangles[i]; + tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, tri.V0 + vertOffset, tri.V1 + vertOffset, tri.V2 + vertOffset)); + } + } + + if (def.DoodadSet < root->DoodadSets.size()) + { + DoodadSet set = root->DoodadSets[def.DoodadSet]; + std::vector<DoodadInstance> instances; + instances.reserve(set.CountInstances); + for (uint32 i = set.FirstInstanceIndex; i < (set.CountInstances + set.FirstInstanceIndex); i++) + { + if (i >= root->DoodadInstances.size()) + break; + instances.push_back(root->DoodadInstances[i]); + } + + for (std::vector<DoodadInstance>::iterator instance = instances.begin(); instance != instances.end(); ++instance) + { + Model* model = Cache->ModelCache.Get(instance->File); + if (!model) + { + model = new Model(instance->File); + Cache->ModelCache.Insert(instance->File, model); + } + + if (!model->IsCollidable) + continue; + G3D::Matrix4 doodadTransformation = Utils::GetWmoDoodadTransformation(*instance, def); + int vertOffset = verts.size(); + for (std::vector<Vector3>::iterator itr2 = model->Vertices.begin(); itr2 != model->Vertices.end(); ++itr2) + verts.push_back(Utils::VectorTransform(*itr2, doodadTransformation)); + for (std::vector<Triangle<uint16> >::iterator itr2 = model->Triangles.begin(); itr2 != model->Triangles.end(); ++itr2) + tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WMO, itr2->V0 + vertOffset, itr2->V1 + vertOffset, itr2->V2 + vertOffset)); + } + + for (std::vector<WorldModelGroup>::iterator group = root->Groups.begin(); group != root->Groups.end(); ++group) + { + if (!group->HasLiquidData) + continue; + + for (uint32 y = 0; y < group->LiquidDataHeader.Height; y++) + { + for (uint32 x = 0; x < group->LiquidDataHeader.Width; x++) + { + if (!group->LiquidDataGeometry.ShouldRender(x, y)) + continue; + + uint32 vertOffset = verts.size(); + verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, + group->LiquidDataGeometry.HeightMap[x][y], x, y)); + verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, + group->LiquidDataGeometry.HeightMap[x + 1][y], x + 1, y)); + verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, + group->LiquidDataGeometry.HeightMap[x][y + 1], x, y + 1)); + verts.push_back(Utils::GetLiquidVert(transformation, group->LiquidDataHeader.BaseLocation, + group->LiquidDataGeometry.HeightMap[x + 1][y + 1], x + 1, y + 1)); + + tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset, vertOffset + 2, vertOffset + 1)); + tris.push_back(Triangle<uint32>(Constants::TRIANGLE_TYPE_WATER, vertOffset + 2, vertOffset + 3, vertOffset + 1)); + + } + } + } + } +} + +void WorldModelHandler::ReadDefinitions() +{ + Chunk* chunk = Source->ObjectData->GetChunkByName("MODF"); + if (!chunk) + return; + + const int32 definitionSize = 64; + uint32 definitionCount = chunk->Length / definitionSize; + _definitions = new std::vector<WorldModelDefinition>; + _definitions->reserve(definitionCount); + FILE* stream = chunk->GetStream(); + for (uint32 i = 0; i < definitionCount; i++) + _definitions->push_back(WorldModelDefinition::Read(stream)); +} + +void WorldModelHandler::ReadModelPaths() +{ + Chunk* mwid = Source->ObjectData->GetChunkByName("MWID"); + Chunk* mwmo = Source->ObjectData->GetChunkByName("MWMO"); + if (!mwid || !mwmo) + return; + + uint32 paths = mwid->Length / 4; + _paths = new std::vector<std::string>; + _paths->reserve(paths); + for (uint32 i = 0; i < paths; i++) + { + FILE* stream = mwid->GetStream(); + fseek(stream, i * 4, SEEK_CUR); + uint32 offset; + if (fread(&offset, sizeof(uint32), 1, stream) != 1) + printf("WorldModelDefinition::Read: Error reading data, expected 1, read 0\n"); + FILE* dataStream = mwmo->GetStream(); + fseek(dataStream, offset + mwmo->Offset, SEEK_SET); + _paths->push_back(Utils::ReadString(dataStream)); + } +} + +WorldModelHandler::~WorldModelHandler() +{ + delete _definitions; + delete _paths; +} diff --git a/src/tools/mesh_extractor/WorldModelHandler.h b/src/tools/mesh_extractor/WorldModelHandler.h new file mode 100644 index 00000000000..29715ded696 --- /dev/null +++ b/src/tools/mesh_extractor/WorldModelHandler.h @@ -0,0 +1,47 @@ +#ifndef WMODEL_HNDL_H +#define WMODEL_HNDL_H +#include "Define.h" +#include "Utils.h" +#include "WorldModelRoot.h" +#include "ObjectDataHandler.h" + +#include <set> +#include <vector> + +class ADT; + +struct WorldModelDefinition : public IDefinition +{ +public: + WorldModelDefinition() {} + + uint32 MwidIndex; + uint32 UniqueId; + Vector3 UpperExtents; + Vector3 LowerExtents; + uint16 Flags; + uint16 DoodadSet; + + static WorldModelDefinition Read(FILE* file); +}; + +class WorldModelHandler : public ObjectDataHandler +{ +public: + WorldModelHandler(ADT* adt); + ~WorldModelHandler(); + + std::vector<Vector3> Vertices; + std::vector<Triangle<uint32> > Triangles; + bool IsSane() { return _definitions && _paths; } + void InsertModelGeometry(std::vector<Vector3>& verts, std::vector<Triangle<uint32> >& tris, WorldModelDefinition& def, WorldModelRoot* root); +protected: + void ProcessInternal(ChunkedData* data); +private: + void ReadDefinitions(); + void ReadModelPaths(); + std::set<uint32> _drawn; + std::vector<WorldModelDefinition>* _definitions; + std::vector<std::string>* _paths; +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/WorldModelRoot.cpp b/src/tools/mesh_extractor/WorldModelRoot.cpp new file mode 100644 index 00000000000..c34a77e4531 --- /dev/null +++ b/src/tools/mesh_extractor/WorldModelRoot.cpp @@ -0,0 +1,74 @@ +#include "WorldModelRoot.h" +#include "ChunkedData.h" +#include "Utils.h" + +WorldModelRoot::WorldModelRoot( std::string path ) +{ + Data = new ChunkedData(path); + Path = path; + ReadHeader(); + ReadGroups(); + ReadDoodadInstances(); + ReadDoodadSets(); +} + +void WorldModelRoot::ReadGroups() +{ + std::string pathBase = Utils::GetPathBase(Path); + Groups.reserve(Header.CountGroups); + for (uint32 i = 0; i < Header.CountGroups; i++) + { + char name[200]; + sprintf(name, "%s_%03u.wmo", pathBase.c_str(), i); + WorldModelGroup group(name, i); + if (!group.IsBad) + Groups.push_back(group); + } +} + +void WorldModelRoot::ReadDoodadSets() +{ + Chunk* chunk = Data->GetChunkByName("MODS"); + if (!chunk) + return; + + FILE* stream = chunk->GetStream(); + ASSERT(chunk->Length / 32 == Header.CountSets && "chunk.Length / 32 == Header.CountSets"); + DoodadSets.reserve(Header.CountSets); + for (uint32 i = 0; i < Header.CountSets; i++) + DoodadSets.push_back(DoodadSet::Read(stream)); +} + +void WorldModelRoot::ReadDoodadInstances() +{ + Chunk* chunk = Data->GetChunkByName("MODD"); + Chunk* nameChunk = Data->GetChunkByName("MODN"); + if (!chunk || !nameChunk) + return; + + const uint32 instanceSize = 40; + uint32 countInstances = chunk->Length / instanceSize; + DoodadInstances.reserve(countInstances); + for (uint32 i = 0; i < countInstances; i++) + { + FILE* stream = chunk->GetStream(); + fseek(stream, instanceSize * i, SEEK_CUR); + DoodadInstance instance = DoodadInstance::Read(stream); + FILE* nameStream = nameChunk->GetStream(); + if (instance.FileOffset >= nameChunk->Length) + continue; + fseek(nameStream, instance.FileOffset, SEEK_CUR); + instance.File = Utils::ReadString(nameStream); + DoodadInstances.push_back(instance); + } +} + +void WorldModelRoot::ReadHeader() +{ + Chunk* chunk = Data->GetChunkByName("MOHD"); + if (!chunk) + return; + + FILE* stream = chunk->GetStream(); + Header = WorldModelHeader::Read(stream); +} diff --git a/src/tools/mesh_extractor/WorldModelRoot.h b/src/tools/mesh_extractor/WorldModelRoot.h new file mode 100644 index 00000000000..c06ff3d5d2b --- /dev/null +++ b/src/tools/mesh_extractor/WorldModelRoot.h @@ -0,0 +1,26 @@ +#ifndef WMOROOT_H +#define WMOROOT_H +#include <string> +#include <vector> + +#include "ChunkedData.h" +#include "Utils.h" +#include "WorldModelGroup.h" + +class WorldModelRoot +{ +public: + WorldModelRoot(std::string path); + std::string Path; + ChunkedData* Data; + WorldModelHeader Header; + std::vector<DoodadInstance> DoodadInstances; + std::vector<DoodadSet> DoodadSets; + std::vector<WorldModelGroup> Groups; +private: + void ReadGroups(); + void ReadDoodadSets(); + void ReadDoodadInstances(); + void ReadHeader(); +}; +#endif
\ No newline at end of file diff --git a/src/tools/mesh_extractor/readme b/src/tools/mesh_extractor/readme new file mode 100644 index 00000000000..85cd7cfc975 --- /dev/null +++ b/src/tools/mesh_extractor/readme @@ -0,0 +1,6 @@ +Experimental mesh extractor. +Original work in C# by stschake +Thanks to: +Subv +~ +For helping in the porting to C++
\ No newline at end of file diff --git a/src/tools/mmaps_generator/CMakeLists.txt b/src/tools/mmaps_generator/CMakeLists.txt new file mode 100644 index 00000000000..b168691c994 --- /dev/null +++ b/src/tools/mmaps_generator/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (C) 2008-2013 TrinityCore <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 mmap_gen_sources *.cpp *.h) + +set(mmap_gen_Includes + ${CMAKE_BINARY_DIR} + ${ACE_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/dep/libmpq + ${CMAKE_SOURCE_DIR}/dep/zlib + ${CMAKE_SOURCE_DIR}/dep/bzip2 + ${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/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 +) + +if( WIN32 ) + set(mmap_gen_Includes + ${mmap_gen_Includes} + ${CMAKE_SOURCE_DIR}/dep/libmpq/win + ) +endif() + +include_directories(${mmap_gen_Includes}) + +add_executable(mmaps_generator ${mmap_gen_sources}) + +target_link_libraries(mmaps_generator + collision + g3dlib + Recast + Detour + ${ACE_LIBRARY} + ${BZIP2_LIBRARIES} + ${ZLIB_LIBRARIES} +) + +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..a490273ad80 --- /dev/null +++ b/src/tools/mmaps_generator/IntermediateValues.cpp @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2008-2013 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); + + std::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..89a5c3ae4c2 --- /dev/null +++ b/src/tools/mmaps_generator/IntermediateValues.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008-2013 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() : heightfield(NULL), compactHeightfield(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..4a016f267b4 --- /dev/null +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -0,0 +1,969 @@ +/* + * Copyright (C) 2008-2013 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 "DetourNavMeshBuilder.h" +#include "DetourNavMesh.h" +#include "DetourCommon.h" + +#include "DisableMgr.h" +#include <ace/OS_NS_unistd.h> + +uint32 GetLiquidFlags(uint32 /*liquidType*/) { return 0; } +namespace DisableMgr +{ + bool IsDisabledFor(DisableType /*type*/, uint32 /*entry*/, Unit const* /*unit*/, uint8 /*flags*/ /*= 0*/) { return false; } +} + +#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) {} +}; + +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_offMeshFilePath (offMeshFilePath), + m_skipContinents (skipContinents), + m_skipJunkMaps (skipJunkMaps), + m_skipBattlegrounds (skipBattlegrounds), + m_maxWalkableAngle (maxWalkableAngle), + m_bigBaseUnit (bigBaseUnit), + m_rcContext (NULL) + { + 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() + { + std::vector<std::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(std::pair<uint32, std::set<uint32>*>(mapID, new std::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(std::pair<uint32, std::set<uint32>*>(mapID, new std::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) + { + std::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); + } + + /**************************************************************************/ + std::set<uint32>* MapBuilder::getTileList(uint32 mapID) + { + TileList::iterator itr = m_tiles.find(mapID); + if (itr != m_tiles.end()) + return (*itr).second; + + std::set<uint32>* tiles = new std::set<uint32>(); + m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, tiles)); + return tiles; + } + + /**************************************************************************/ + void MapBuilder::buildAllMaps(int threads) + { + std::vector<BuilderThread*> _threads; + + BuilderThreadPool* pool = threads > 0 ? new BuilderThreadPool() : NULL; + + for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) + { + uint32 mapID = it->first; + if (!shouldSkipMap(mapID)) + { + if (threads > 0) + pool->Enqueue(new MapBuildRequest(mapID)); + else + buildMap(mapID); + } + } + + for (int i = 0; i < threads; ++i) + _threads.push_back(new BuilderThread(this, pool->Queue())); + + // Free memory + for (std::vector<BuilderThread*>::iterator _th = _threads.begin(); _th != _threads.end(); ++_th) + { + (*_th)->wait(); + delete *_th; + } + + delete pool; + } + + /**************************************************************************/ + 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; + if (fread(&mapId, sizeof(int), 1, file) != 1) + return; + if (fread(&tileX, sizeof(int), 1, file) != 1) + return; + if (fread(&tileY, sizeof(int), 1, file) != 1) + return; + + dtNavMesh* navMesh = NULL; + buildNavMesh(mapId, navMesh); + if (!navMesh) + { + printf("Failed creating navmesh! \n"); + fclose(file); + return; + } + + uint32 verticesCount, indicesCount; + if (fread(&verticesCount, sizeof(uint32), 1, file) != 1) + return; + if (fread(&indicesCount, sizeof(uint32), 1, file) != 1) + return; + + float* verts = new float[verticesCount]; + int* inds = new int[indicesCount]; + + if (fread(verts, sizeof(float), verticesCount, file) != verticesCount) + return; + if (fread(inds, sizeof(int), indicesCount, file) != indicesCount) + return; + + MeshData data; + + for (uint32 i = 0; i < verticesCount; ++i) + data.solidVerts.append(verts[i]); + + for (uint32 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 %u] Building map %03u:\n", uint32(ACE_Thread::self()), mapID); + + std::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 (std::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) + { + std::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 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 (std::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] + float(x*config.tileSize - config.borderSize)*config.cs; + tileCfg.bmin[2] = config.bmin[2] + float(y*config.tileSize - config.borderSize)*config.cs; + tileCfg.bmax[0] = config.bmin[0] + float((x+1)*config.tileSize + config.borderSize)*config.cs; + tileCfg.bmax[2] = config.bmin[2] + float((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; + int count = fread(&header, sizeof(MmapTileHeader), 1, file); + fclose(file); + if (count != 1) + return false; + + 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..3ffaea0ab66 --- /dev/null +++ b/src/tools/mmaps_generator/MapBuilder.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2008-2013 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 "Recast.h" +#include "DetourNavMesh.h" + +#include <ace/Task.h> +#include <ace/Activation_Queue.h> +#include <ace/Method_Request.h> + +using namespace VMAP; + +// G3D namespace typedefs conflicts with ACE typedefs + +namespace MMAP +{ + typedef std::map<uint32, std::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(); + std::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 MapBuildRequest : public ACE_Method_Request + { + public: + MapBuildRequest(uint32 mapId) : _mapId(mapId) {} + + virtual int call() + { + /// @ Actually a creative way of unabstracting the class and returning a member variable + return (int)_mapId; + } + + private: + uint32 _mapId; + }; + + class BuilderThread : public ACE_Task_Base + { + private: + MapBuilder* _builder; + ACE_Activation_Queue* _queue; + + public: + BuilderThread(MapBuilder* builder, ACE_Activation_Queue* queue) : _builder(builder), _queue(queue) { activate(); } + + int svc() + { + /// @ Set a timeout for dequeue attempts (only used when the queue is empty) as it will never get populated after thread starts + ACE_Time_Value timeout(5); + ACE_Method_Request* request = NULL; + while ((request = _queue->dequeue(&timeout)) != NULL) + { + _builder->buildMap(request->call()); + delete request; + request = NULL; + } + + return 0; + } + }; + + class BuilderThreadPool + { + public: + BuilderThreadPool() : _queue(new ACE_Activation_Queue()) {} + ~BuilderThreadPool() { _queue->queue()->close(); delete _queue; } + + void Enqueue(MapBuildRequest* request) + { + _queue->enqueue(request); + } + + ACE_Activation_Queue* Queue() { return _queue; } + + private: + ACE_Activation_Queue* _queue; + }; +} + +#endif diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h new file mode 100644 index 00000000000..3e06ff58410 --- /dev/null +++ b/src/tools/mmaps_generator/PathCommon.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2008-2013 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 <ace/OS_NS_sys_time.h> + +#include "Define.h" + +#ifndef _WIN32 + #include <stddef.h> + #include <dirent.h> +#endif + +#ifdef __linux__ + #include <errno.h> +#endif + +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 +}; + +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(std::vector<std::string> &fileList, std::string dirpath = ".", std::string filter = "*") + { + #ifdef WIN32 + HANDLE hFind; + WIN32_FIND_DATA findFileInfo; + std::string directory; + + directory = dirpath + "/" + filter; + + hFind = FindFirstFile(directory.c_str(), &findFileInfo); + + if (hFind == INVALID_HANDLE_VALUE) + return LISTFILE_DIRECTORY_NOT_FOUND; + do + { + if ((findFileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + fileList.push_back(std::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(std::string(dp->d_name)); + } + else + break; + } + + if (dirp) + closedir(dirp); + else + return LISTFILE_DIRECTORY_NOT_FOUND; + #endif + + return LISTFILE_OK; + } + + inline uint32 getMSTime() + { + static const ACE_Time_Value ApplicationStartTime = ACE_OS::gettimeofday(); + return (ACE_OS::gettimeofday() - ApplicationStartTime).msec(); + } + + inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) + { + // getMSTime() have limited data range and this is case when it overflow in this tick + if (oldMSTime > newMSTime) + return (0xFFFFFFFF - oldMSTime) + newMSTime; + else + return newMSTime - oldMSTime; + } + + inline uint32 GetMSTimeDiffToNow(uint32 oldMSTime) + { + return getMSTimeDiff(oldMSTime, getMSTime()); + } +} + +#endif diff --git a/src/tools/mmaps_generator/PathGenerator.cpp b/src/tools/mmaps_generator/PathGenerator.cpp new file mode 100644 index 00000000000..47d35b517d5 --- /dev/null +++ b/src/tools/mmaps_generator/PathGenerator.cpp @@ -0,0 +1,296 @@ +/* + * 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" + +using namespace MMAP; + +bool checkDirectories(bool debugOutput) +{ + std::vector<std::string> dirFiles; + + if (getDirContents(dirFiles, "maps") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty()) + { + printf("'maps' directory is empty or does not exist\n"); + return false; + } + + dirFiles.clear(); + if (getDirContents(dirFiles, "vmaps", "*.vmtree") == LISTFILE_DIRECTORY_NOT_FOUND || dirFiles.empty()) + { + 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 0; +} diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp new file mode 100644 index 00000000000..a42cd2b9bc6 --- /dev/null +++ b/src/tools/mmaps_generator/TerrainBuilder.cpp @@ -0,0 +1,933 @@ +/* + * Copyright (C) 2008-2013 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" +#include <vector> + +// ****************************************** +// Map file format defines +// ****************************************** +struct map_fileheader +{ + uint32 mapMagic; + uint32 versionMagic; + uint32 buildMagic; + uint32 areaMapOffset; + uint32 areaMapSize; + uint32 heightMapOffset; + uint32 heightMapSize; + uint32 liquidMapOffset; + uint32 liquidMapSize; + uint32 holesOffset; + uint32 holesSize; +}; + +#define MAP_HEIGHT_NO_HEIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 + +struct map_heightHeader +{ + uint32 fourcc; + uint32 flags; + float gridHeight; + float gridMaxHeight; +}; + +#define MAP_LIQUID_NO_TYPE 0x0001 +#define MAP_LIQUID_NO_HEIGHT 0x0002 + +struct map_liquidHeader +{ + uint32 fourcc; + uint16 flags; + uint16 liquidType; + uint8 offsetX; + uint8 offsetY; + uint8 width; + uint8 height; + float liquidLevel; +}; + +#define MAP_LIQUID_TYPE_NO_WATER 0x00 +#define MAP_LIQUID_TYPE_WATER 0x01 +#define MAP_LIQUID_TYPE_OCEAN 0x02 +#define MAP_LIQUID_TYPE_MAGMA 0x04 +#define MAP_LIQUID_TYPE_SLIME 0x08 +#define MAP_LIQUID_TYPE_DARK_WATER 0x10 +#define MAP_LIQUID_TYPE_WMO_WATER 0x20 + +namespace MMAP +{ + + char const* MAP_VERSION_MAGIC = "v1.3"; + + 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; + if (fread(&fheader, sizeof(map_fileheader), 1, mapFile) != 1 || + 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); + + bool haveTerrain = false; + bool haveLiquid = false; + if (fread(&hheader, sizeof(map_heightHeader), 1, mapFile) == 1) + { + haveTerrain = !(hheader.flags & MAP_HEIGHT_NO_HEIGHT); + 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) + { + float heightMultiplier; + float V9[V9_SIZE_SQ], V8[V8_SIZE_SQ]; + int expected = V9_SIZE_SQ + V8_SIZE_SQ; + + if (hheader.flags & MAP_HEIGHT_AS_INT8) + { + uint8 v9[V9_SIZE_SQ]; + uint8 v8[V8_SIZE_SQ]; + int count = 0; + count += fread(v9, sizeof(uint8), V9_SIZE_SQ, mapFile); + count += fread(v8, sizeof(uint8), V8_SIZE_SQ, mapFile); + if (count != expected) + printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count); + + heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 255; + + for (int i = 0; i < V9_SIZE_SQ; ++i) + V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight; + + for (int 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]; + int count = 0; + count += fread(v9, sizeof(uint16), V9_SIZE_SQ, mapFile); + count += fread(v8, sizeof(uint16), V8_SIZE_SQ, mapFile); + if (count != expected) + printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count); + + heightMultiplier = (hheader.gridMaxHeight - hheader.gridHeight) / 65535; + + for (int i = 0; i < V9_SIZE_SQ; ++i) + V9[i] = (float)v9[i]*heightMultiplier + hheader.gridHeight; + + for (int i = 0; i < V8_SIZE_SQ; ++i) + V8[i] = (float)v8[i]*heightMultiplier + hheader.gridHeight; + } + else + { + int count = 0; + count += fread(V9, sizeof(float), V9_SIZE_SQ, mapFile); + count += fread(V8, sizeof(float), V8_SIZE_SQ, mapFile); + if (count != expected) + printf("TerrainBuilder::loadMap: Failed to read some data expected %d, read %d\n", expected, count); + } + + // hole data + if (fheader.holesSize != 0) + { + memset(holes, 0, fheader.holesSize); + fseek(mapFile, fheader.holesOffset, SEEK_SET); + if (fread(holes, fheader.holesSize, 1, mapFile) != 1) + printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + } + + int count = meshData.solidVerts.size() / 3; + float xoffset = (float(tileX)-32)*GRID_SIZE; + float yoffset = (float(tileY)-32)*GRID_SIZE; + + float coord[3]; + + for (int 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 (int 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 indices[3], loopStart = 0, loopEnd = 0, loopInc = 0; + getLoopVars(portion, loopStart, loopEnd, loopInc); + for (int i = loopStart; i < loopEnd; i+=loopInc) + for (int 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); + if (fread(&lheader, sizeof(map_liquidHeader), 1, mapFile) != 1) + printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + + + float* liquid_map = NULL; + + if (!(lheader.flags & MAP_LIQUID_NO_TYPE)) + if (fread(liquid_type, sizeof(liquid_type), 1, mapFile) != 1) + printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + + if (!(lheader.flags & MAP_LIQUID_NO_HEIGHT)) + { + uint32 toRead = lheader.width * lheader.height; + liquid_map = new float [toRead]; + if (fread(liquid_map, sizeof(float), toRead, mapFile) != toRead) + printf("TerrainBuilder::loadMap: Failed to read some data expected 1, read 0\n"); + } + + if (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 = 0, loopEnd = 0, loopInc = 0, triInc = BOTTOM-TOP; + getLoopVars(portion, loopStart, loopEnd, loopInc); + + // 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 = 0, loopEnd = 0, loopInc = 0, 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; + // FIXME: "warning: the address of ‘liquid_type’ will always evaluate as ‘true’" + + // if there is no liquid, don't use liquid + if (!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; + + std::vector<GroupModel> groupModels; + worldModel->getGroupModels(groupModels); + + // all M2s need to have triangle indices reversed + bool isM2 = instance.name.find(".m2") != std::string::npos || instance.name.find(".M2") != std::string::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 (std::vector<GroupModel>::iterator it = groupModels.begin(); it != groupModels.end(); ++it) + { + std::vector<Vector3> tempVertices; + std::vector<Vector3> transformedVertices; + std::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) + { + std::vector<Vector3> liqVerts; + std::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(std::vector<Vector3> &source, std::vector<Vector3> &transformedVertices, float scale, G3D::Matrix3 &rotation, Vector3 &position) + { + for (std::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(std::vector<Vector3> &source, G3D::Array<float> &dest) + { + for (std::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(std::vector<MeshTriangle> &source, G3D::Array<int> &dest, int offset, bool flip) + { + if (flip) + { + for (std::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 (std::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) + { + std::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) + { + std::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]; + uint32 mid, tx, ty; + float size; + if (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) != 10) + 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..069a5a94c84 --- /dev/null +++ b/src/tools/mmaps_generator/TerrainBuilder.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2008-2013 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 "WorldModel.h" + +#include "G3D/Array.h" +#include "G3D/Vector3.h" +#include "G3D/Matrix3.h" + +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 + + 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(std::vector<G3D::Vector3> &original, std::vector<G3D::Vector3> &transformed, + float scale, G3D::Matrix3 &rotation, G3D::Vector3 &position); + static void copyVertices(std::vector<G3D::Vector3> &source, G3D::Array<float> &dest); + static void copyIndices(std::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..972f222bba6 --- /dev/null +++ b/src/tools/mmaps_generator/VMapExtensions.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008-2013 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" + +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(std::vector<GroupModel> &groupModels) + { + groupModels = this->groupModels; + } + + // declared in src/shared/vmap/WorldModel.h + void GroupModel::getMeshData(std::vector<Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid) + { + vertices = this->vertices; + triangles = this->triangles; + liquid = iLiquid; + } + + // declared in src/shared/vmap/ModelInstance.h + WorldModel* 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; + } +} diff --git a/src/tools/vmap4_assembler/CMakeLists.txt b/src/tools/vmap4_assembler/CMakeLists.txt index 55b6514d091..8d455eae6ee 100644 --- a/src/tools/vmap4_assembler/CMakeLists.txt +++ b/src/tools/vmap4_assembler/CMakeLists.txt @@ -20,9 +20,7 @@ include_directories( ${ZLIB_INCLUDE_DIR} ) -add_definitions(-DNO_CORE_FUNCS) add_executable(vmap4assembler VMapAssembler.cpp) -add_dependencies(vmap4assembler mpq) if(CMAKE_SYSTEM_NAME MATCHES "Darwin") set_target_properties(vmap4assembler PROPERTIES LINK_FLAGS "-framework Carbon") diff --git a/src/tools/vmap4_extractor/CMakeLists.txt b/src/tools/vmap4_extractor/CMakeLists.txt index 19798e46475..023f5447f85 100644 --- a/src/tools/vmap4_extractor/CMakeLists.txt +++ b/src/tools/vmap4_extractor/CMakeLists.txt @@ -11,26 +11,19 @@ file(GLOB_RECURSE sources *.cpp *.h) -# uncomment next line to disable debug mode -add_definitions("-DIOMAP_DEBUG") - -# build setup currently only supports libmpq 0.4.x -add_definitions("-DUSE_LIBMPQ04") -add_definitions("-Wall") -add_definitions("-ggdb") -add_definitions("-O3") - -if( UNIX ) - include_directories( - ${CMAKE_SOURCE_DIR}/dep/libmpq - ) -elseif( WIN32 ) - include_directories( +set(include_Dirs ${CMAKE_SOURCE_DIR}/dep/libmpq +) + +if( WIN32 ) + set(include_Dirs + ${include_Dirs} ${CMAKE_SOURCE_DIR}/dep/libmpq/win ) endif() +include_directories(${include_Dirs}) + add_executable(vmap4extractor ${sources}) target_link_libraries(vmap4extractor @@ -39,8 +32,6 @@ target_link_libraries(vmap4extractor ${ZLIB_LIBRARIES} ) -add_dependencies(vmap4extractor mpq) - if( UNIX ) install(TARGETS vmap4extractor DESTINATION bin) elseif( WIN32 ) diff --git a/src/tools/vmap4_extractor/adtfile.cpp b/src/tools/vmap4_extractor/adtfile.cpp index a605118eead..56e62d474d1 100644 --- a/src/tools/vmap4_extractor/adtfile.cpp +++ b/src/tools/vmap4_extractor/adtfile.cpp @@ -69,8 +69,7 @@ void fixname2(char* name, size_t len) char* GetExtension(char* FileName) { - char* szTemp; - if (szTemp = strrchr(FileName, '.')) + if (char* szTemp = strrchr(FileName, '.')) return szTemp; return NULL; } diff --git a/src/tools/vmap4_extractor/model.cpp b/src/tools/vmap4_extractor/model.cpp index c642f73ccae..b950db023fe 100644 --- a/src/tools/vmap4_extractor/model.cpp +++ b/src/tools/vmap4_extractor/model.cpp @@ -152,10 +152,10 @@ ModelInstance::ModelInstance(MPQFile& f, char const* ModelInstName, uint32 mapID fseek(input, 8, SEEK_SET); // get the correct no of vertices int nVertices; - fread(&nVertices, sizeof (int), 1, input); + int count = fread(&nVertices, sizeof (int), 1, input); fclose(input); - if(nVertices == 0) + if (count != 1 || nVertices == 0) return; uint16 adtId = 0;// not used for models diff --git a/src/tools/vmap4_extractor/mpq_libmpq.cpp b/src/tools/vmap4_extractor/mpq_libmpq.cpp index 528b9679a58..ffa097d9a22 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq.cpp +++ b/src/tools/vmap4_extractor/mpq_libmpq.cpp @@ -79,7 +79,7 @@ size_t MPQFile::read(void* dest, size_t bytes) if (eof) return 0; size_t rpos = pointer + bytes; - if (rpos > size) { + if (rpos > size_t(size)) { bytes = size - pointer; eof = true; } diff --git a/src/tools/vmap4_extractor/mpq_libmpq04.h b/src/tools/vmap4_extractor/mpq_libmpq04.h index 89f715e9e87..9f0163067c4 100644 --- a/src/tools/vmap4_extractor/mpq_libmpq04.h +++ b/src/tools/vmap4_extractor/mpq_libmpq04.h @@ -1,6 +1,3 @@ -#define _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_WARNINGS - #ifndef MPQ_H #define MPQ_H diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp index 186f9c8606e..89e4b850dac 100644 --- a/src/tools/vmap4_extractor/vmapexport.cpp +++ b/src/tools/vmap4_extractor/vmapexport.cpp @@ -461,8 +461,7 @@ int main(int argc, char ** argv) printf("Your output directory seems to be polluted, please use an empty directory!\n"); printf("<press return to exit>"); char garbage[2]; - scanf("%c", garbage); - return 1; + return scanf("%c", garbage); } } diff --git a/src/tools/vmap4_extractor/wdtfile.cpp b/src/tools/vmap4_extractor/wdtfile.cpp index 7420edfee2e..d9216fd77eb 100644 --- a/src/tools/vmap4_extractor/wdtfile.cpp +++ b/src/tools/vmap4_extractor/wdtfile.cpp @@ -35,7 +35,7 @@ WDTFile::WDTFile(char* file_name, char* file_name1) : WDT(file_name), gWmoInstan filename.append(file_name1,strlen(file_name1)); } -bool WDTFile::init(char */*map_id*/, unsigned int mapID) +bool WDTFile::init(char* /*map_id*/, unsigned int mapID) { if (WDT.isEof()) { diff --git a/src/tools/vmap4_extractor/wmo.cpp b/src/tools/vmap4_extractor/wmo.cpp index 0bc749b9bd5..7c9f23c5a95 100644 --- a/src/tools/vmap4_extractor/wmo.cpp +++ b/src/tools/vmap4_extractor/wmo.cpp @@ -521,10 +521,10 @@ WMOInstance::WMOInstance(MPQFile& f, char const* WmoInstName, uint32 mapID, uint fseek(input, 8, SEEK_SET); // get the correct no of vertices int nVertices; - fread(&nVertices, sizeof (int), 1, input); + int count = fread(&nVertices, sizeof (int), 1, input); fclose(input); - if(nVertices == 0) + if (count != 1 || nVertices == 0) return; float x,z; |