diff options
Diffstat (limited to 'src')
127 files changed, 2455 insertions, 1350 deletions
diff --git a/src/cmake/compiler/msvc/settings.cmake b/src/cmake/compiler/msvc/settings.cmake index caca79ee37..7ce1f7f200 100644 --- a/src/cmake/compiler/msvc/settings.cmake +++ b/src/cmake/compiler/msvc/settings.cmake @@ -59,7 +59,7 @@ endif() # Fixes a compiler-problem when using PCH - the /Ym flag is adjusted by the compiler in MSVC2012, hence we need to set an upper limit with /Zm to avoid discrepancies) # (And yes, this is a verified , unresolved bug with MSVC... *sigh*) string(REGEX REPLACE "/Zm[0-9]+ *" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm500" CACHE STRING "" FORCE) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zm500") # Enable and treat as errors the following warnings to easily detect virtual function signature failures: # 'function' : member function does not override any base class virtual member function diff --git a/src/common/Collision/Management/MMapManager.h b/src/common/Collision/Management/MMapManager.h index f12cd11841..842fe47fbf 100644 --- a/src/common/Collision/Management/MMapManager.h +++ b/src/common/Collision/Management/MMapManager.h @@ -14,7 +14,7 @@ #include "World.h" // memory management -inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/) +inline void* dtCustomAlloc(size_t size, dtAllocHint /*hint*/) { return (void*)new unsigned char[size]; } diff --git a/src/common/Collision/Management/VMapManager2.cpp b/src/common/Collision/Management/VMapManager2.cpp index 8dc9d770de..6897ffa56a 100644 --- a/src/common/Collision/Management/VMapManager2.cpp +++ b/src/common/Collision/Management/VMapManager2.cpp @@ -1,7 +1,19 @@ /* - * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2 - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * Copyright (C) + * Copyright (C) + * + * 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 <iostream> @@ -26,6 +38,7 @@ namespace VMAP { VMapManager2::VMapManager2() { + GetLiquidFlagsPtr = &GetLiquidFlagsDummy; } VMapManager2::~VMapManager2(void) @@ -76,7 +89,7 @@ namespace VMAP } // load one tile (internal use only) - bool VMapManager2::_loadMap(unsigned int mapId, const std::string& basePath, uint32 tileX, uint32 tileY) + bool VMapManager2::_loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY) { InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId); if (instanceTree == iInstanceMapTrees.end()) @@ -84,10 +97,10 @@ namespace VMAP std::string mapFileName = getMapFileName(mapId); StaticMapTree* newTree = new StaticMapTree(mapId, basePath); if (!newTree->InitMap(mapFileName, this)) - { - delete newTree; + { + delete newTree; return false; - } + } instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, newTree)).first; } @@ -236,7 +249,7 @@ namespace VMAP floor = info.ground_Z; ASSERT(floor < std::numeric_limits<float>::max()); type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc - if (reqLiquidType && !(GetLiquidFlags(type) & reqLiquidType)) + if (reqLiquidType && !(GetLiquidFlagsPtr(type) & reqLiquidType)) return false; if (info.hitInstance->GetLiquidLevel(pos, info, level)) return true; @@ -276,7 +289,6 @@ namespace VMAP { //! Critical section, thread safe access to iLoadedModelFiles TRINITY_GUARD(ACE_Thread_Mutex, LoadedModelFilesLock); - ModelFileMap::iterator model = iLoadedModelFiles.find(filename); if (model == iLoadedModelFiles.end()) { @@ -298,4 +310,4 @@ namespace VMAP return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y); } -} // namespace VMAP +} // namespace VMAP
\ No newline at end of file diff --git a/src/common/Collision/Management/VMapManager2.h b/src/common/Collision/Management/VMapManager2.h index f4455fc72d..ac07d79458 100644 --- a/src/common/Collision/Management/VMapManager2.h +++ b/src/common/Collision/Management/VMapManager2.h @@ -1,7 +1,19 @@ /* - * Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2 - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * Copyright (C) + * Copyright (C) + * + * 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 _VMAPMANAGER2_H @@ -66,6 +78,8 @@ namespace VMAP bool _loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY); /* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */ + static uint32 GetLiquidFlagsDummy(uint32) { return 0; } + public: // public for debug G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const; @@ -102,7 +116,10 @@ namespace VMAP virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y); public: void getInstanceMapTree(InstanceTreeMap &instanceMapTree); + + typedef uint32(*GetLiquidFlagsFn)(uint32 liquidType); + GetLiquidFlagsFn GetLiquidFlagsPtr; }; } -#endif +#endif
\ No newline at end of file diff --git a/src/common/Collision/Maps/TileAssembler.h b/src/common/Collision/Maps/TileAssembler.h index be11c256b5..d93662d7fc 100644 --- a/src/common/Collision/Maps/TileAssembler.h +++ b/src/common/Collision/Maps/TileAssembler.h @@ -34,7 +34,7 @@ namespace VMAP float iScale; void init() { - iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iDir.y/180.f, G3D::pi()*iDir.x/180.f, G3D::pi()*iDir.z/180.f); + iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pif()*iDir.y/180.f, G3D::pif()*iDir.x/180.f, G3D::pif()*iDir.z/180.f); } G3D::Vector3 transform(const G3D::Vector3& pIn) const; void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; } diff --git a/src/common/Database/Implementation/CharacterDatabase.cpp b/src/common/Database/Implementation/CharacterDatabase.cpp index a656b66884..c1ace0ccaa 100644 --- a/src/common/Database/Implementation/CharacterDatabase.cpp +++ b/src/common/Database/Implementation/CharacterDatabase.cpp @@ -136,7 +136,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, "DELETE FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating, maxMMR FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT account, COUNT(guid) FROM characters WHERE account = ? GROUP BY account", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT ? AS account,(SELECT COUNT(*) FROM characters WHERE account =?) AS cnt", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/common/Database/Implementation/LoginDatabase.cpp b/src/common/Database/Implementation/LoginDatabase.cpp index 2f24eb49e0..66d1755c7a 100644 --- a/src/common/Database/Implementation/LoginDatabase.cpp +++ b/src/common/Database/Implementation/LoginDatabase.cpp @@ -57,6 +57,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = online | (1<<(?-1)) WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_OLD_LOGS, "DELETE FROM logs WHERE (time + ?) < ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS, "DELETE FROM account_access WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM, "DELETE FROM account_access WHERE id = ? AND (RealmID = ? OR RealmID = -1)", CONNECTION_ASYNC); diff --git a/src/common/Database/Implementation/LoginDatabase.h b/src/common/Database/Implementation/LoginDatabase.h index d745d44c8f..0bde70d161 100644 --- a/src/common/Database/Implementation/LoginDatabase.h +++ b/src/common/Database/Implementation/LoginDatabase.h @@ -77,6 +77,7 @@ enum LoginDatabaseStatements LOGIN_UPD_MUTE_TIME_LOGIN, LOGIN_UPD_LAST_IP, LOGIN_UPD_ACCOUNT_ONLINE, + LOGIN_UPD_UPTIME_PLAYERS, LOGIN_DEL_OLD_LOGS, LOGIN_DEL_ACCOUNT_ACCESS, LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM, diff --git a/src/common/Database/Implementation/WorldDatabase.cpp b/src/common/Database/Implementation/WorldDatabase.cpp index 77b3ca2a62..2dd28018ed 100644 --- a/src/common/Database/Implementation/WorldDatabase.cpp +++ b/src/common/Database/Implementation/WorldDatabase.cpp @@ -70,8 +70,8 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH); PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id , map, spawnMask, phaseMask, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_GAME_EVENT_CREATURE, "DELETE FROM game_event_creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP, "DELETE FROM game_event_model_equip WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/scripts/Commands/cs_mmaps.cpp b/src/scripts/Commands/cs_mmaps.cpp new file mode 100644 index 0000000000..831539ce2f --- /dev/null +++ b/src/scripts/Commands/cs_mmaps.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2008-2016 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 "DisableMgr.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") { } + + std::vector<ChatCommand> GetCommands() const override + { + static std::vector<ChatCommand> mmapCommandTable = + { + { "loadedtiles", SEC_ADMINISTRATOR, false, &HandleMmapLoadedTilesCommand, "" }, + { "loc", SEC_ADMINISTRATOR, false, &HandleMmapLocCommand, "" }, + { "path", SEC_ADMINISTRATOR, false, &HandleMmapPathCommand, "" }, + { "stats", SEC_ADMINISTRATOR, false, &HandleMmapStatsCommand, "" }, + { "testarea", SEC_ADMINISTRATOR, false, &HandleMmapTestArea, "" }, + }; + + static std::vector<ChatCommand> commandTable = + { + { "mmap", SEC_ADMINISTRATOR, true, NULL, "", mmapCommandTable }, + }; + return commandTable; + } + + static bool HandleMmapPathCommand(ChatHandler* handler, char const* args) + { + if (!MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + handler->PSendSysMessage("mmap path:"); + + // units + Player* player = handler->GetSession()->GetPlayer(); + Unit* target = handler->getSelectedUnit(); + if (!player || !target) + { + handler->PSendSysMessage("Invalid target/source selection."); + return true; + } + + char* para = strtok((char*)args, " "); + + bool useStraightPath = false; + if (para && strcmp(para, "true") == 0) + useStraightPath = true; + + bool useStraightLine = false; + if (para && strcmp(para, "line") == 0) + useStraightLine = 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, false); + + Movement::PointsArray const& pointPath = path.GetPath(); + handler->PSendSysMessage("%s's path to %s:", target->GetName().c_str(), player->GetName().c_str()); + handler->PSendSysMessage("Building: %s", useStraightPath ? "StraightPath" : useStraightLine ? "Raycast" : "SmoothPath"); + handler->PSendSysMessage("Result: %s - Length: %zu - Type: %u", (result ? "true" : "false"), pointPath.size(), path.GetPathType()); + + G3D::Vector3 const &start = path.GetStartPosition(); + G3D::Vector3 const &end = path.GetEndPosition(); + G3D::Vector3 const &actualEnd = path.GetActualEndPosition(); + + handler->PSendSysMessage("StartPosition (%.3f, %.3f, %.3f)", start.x, start.y, start.z); + handler->PSendSysMessage("EndPosition (%.3f, %.3f, %.3f)", end.x, end.y, end.z); + handler->PSendSysMessage("ActualEndPosition (%.3f, %.3f, %.3f)", actualEnd.x, actualEnd.y, actualEnd.z); + + if (!player->IsGameMaster()) + handler->PSendSysMessage("Enable GM mode to see the path points."); + + for (uint32 i = 0; i < pointPath.size(); ++i) + player->SummonCreature(VISUAL_WAYPOINT, pointPath[i].x, pointPath[i].y, pointPath[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 9000); + + return true; + } + + static bool HandleMmapLocCommand(ChatHandler* handler, char const* /*args*/) + { + handler->PSendSysMessage("mmap tileloc:"); + + // grid tile location + Player* player = handler->GetSession()->GetPlayer(); + + int32 gx = 32 - player->GetPositionX() / SIZE_OF_GRIDS; + int32 gy = 32 - player->GetPositionY() / SIZE_OF_GRIDS; + + handler->PSendSysMessage("%03u%02i%02i.mmtile", player->GetMapId(), gx, gy); + handler->PSendSysMessage("gridloc [%i, %i]", gy, gx); + + // calculate navmesh tile location + dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); + dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(handler->GetSession()->GetPlayer()->GetMapId(), player->GetInstanceId()); + if (!navmesh || !navmeshquery) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + float const* min = navmesh->getParams()->orig; + float x, y, z; + player->GetPosition(x, y, z); + float location[VERTEX_SIZE] = {y, z, x}; + float extents[VERTEX_SIZE] = {3.0f, 5.0f, 3.0f}; + + int32 tilex = int32((y - min[0]) / SIZE_OF_GRIDS); + int32 tiley = int32((x - min[2]) / SIZE_OF_GRIDS); + + handler->PSendSysMessage("Calc [%02i, %02i]", tilex, tiley); + + // navmesh poly -> navmesh tile location + dtQueryFilter filter = dtQueryFilter(); + dtPolyRef polyRef = INVALID_POLYREF; + if (dtStatusFailed(navmeshquery->findNearestPoly(location, extents, &filter, &polyRef, NULL))) + { + handler->PSendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)"); + return true; + } + + if (polyRef == INVALID_POLYREF) + handler->PSendSysMessage("Dt [??, ??] (invalid poly, probably no tile loaded)"); + else + { + dtMeshTile const* tile; + dtPoly const* poly; + if (dtStatusSucceed(navmesh->getTileAndPolyByRef(polyRef, &tile, &poly))) + { + if (tile) + { + handler->PSendSysMessage("Dt [%02i,%02i]", tile->header->x, tile->header->y); + return false; + } + } + + handler->PSendSysMessage("Dt [??,??] (no tile loaded)"); + } + + return true; + } + + static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/) + { + uint32 mapid = handler->GetSession()->GetPlayer()->GetMapId(); + dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(mapid); + dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(mapid, handler->GetSession()->GetPlayer()->GetInstanceId()); + if (!navmesh || !navmeshquery) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + handler->PSendSysMessage("mmap loadedtiles:"); + + for (int32 i = 0; i < navmesh->getMaxTiles(); ++i) + { + dtMeshTile const* tile = navmesh->getTile(i); + if (!tile || !tile->header) + continue; + + handler->PSendSysMessage("[%02i, %02i]", tile->header->x, tile->header->y); + } + + return true; + } + + static bool HandleMmapStatsCommand(ChatHandler* handler, char const* /*args*/) + { + handler->PSendSysMessage("mmap stats:"); + //handler->PSendSysMessage(" global mmap pathfinding is %sabled", DisableMgr::IsPathfindingEnabled(mapId) ? "en" : "dis"); + + MMAP::MMapManager* manager = MMAP::MMapFactory::createOrGetMMapManager(); + handler->PSendSysMessage(" %u maps loaded with %u tiles overall", manager->getLoadedMapsCount(), manager->getLoadedTilesCount()); + + dtNavMesh const* navmesh = manager->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId()); + if (!navmesh) + { + handler->PSendSysMessage("NavMesh not loaded for current map."); + return true; + } + + uint32 tileCount = 0; + uint32 nodeCount = 0; + uint32 polyCount = 0; + uint32 vertCount = 0; + uint32 triCount = 0; + uint32 triVertCount = 0; + uint32 dataSize = 0; + for (int32 i = 0; i < navmesh->getMaxTiles(); ++i) + { + dtMeshTile const* tile = navmesh->getTile(i); + if (!tile || !tile->header) + continue; + + tileCount++; + nodeCount += tile->header->bvNodeCount; + polyCount += tile->header->polyCount; + vertCount += tile->header->vertCount; + triCount += tile->header->detailTriCount; + triVertCount += tile->header->detailVertCount; + dataSize += tile->dataSize; + } + + handler->PSendSysMessage("Navmesh stats:"); + handler->PSendSysMessage(" %u tiles loaded", tileCount); + handler->PSendSysMessage(" %u BVTree nodes", nodeCount); + handler->PSendSysMessage(" %u polygons (%u vertices)", polyCount, vertCount); + handler->PSendSysMessage(" %u triangles (%u vertices)", triCount, triVertCount); + handler->PSendSysMessage(" %.2f MB of data (not including pointers)", ((float)dataSize / sizeof(unsigned char)) / 1048576); + + return true; + } + + static bool HandleMmapTestArea(ChatHandler* handler, char const* /*args*/) + { + float radius = 40.0f; + WorldObject* object = handler->GetSession()->GetPlayer(); + + 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 %zu Creatures.", creatureList.size()); + + uint32 paths = 0; + uint32 uStartTime = getMSTime(); + + float gx, gy, gz; + object->GetPosition(gx, gy, gz); + for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + { + PathGenerator path(*itr); + path.CalculatePath(gx, gy, gz); + ++paths; + } + + uint32 uPathLoadTime = getMSTimeDiff(uStartTime, getMSTime()); + handler->PSendSysMessage("Generated %i paths in %i ms", paths, uPathLoadTime); + } + else + handler->PSendSysMessage("No creatures in %f yard range.", radius); + + return true; + } +}; + +void AddSC_mmaps_commandscript() +{ + new mmaps_commandscript(); +} diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 3af8f9adc3..f133d0b35e 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -429,7 +429,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map); return false; } - if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area)) + if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !sAreaTableStore.LookupEntry(e.event.respawn.area)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area); return false; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 66b713d594..3b02da9c64 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -1731,7 +1731,7 @@ class SmartAIMgr bool IsItemValid(SmartScriptHolder const& e, uint32 entry) { - if (!sItemStore.LookupEntry(entry)) + if (!sObjectMgr->GetItemTemplate(entry)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Item entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry); return false; diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 881bc09ce2..ac8d218c1b 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -167,7 +167,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) return true; } case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA: - if (!GetAreaEntryByAreaID(area.id)) + if (!sAreaTableStore.LookupEntry(area.id)) { sLog->outErrorDb("Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA (%u) has wrong area id in value1 (%u), ignored.", criteria->ID, criteria->requiredType, dataType, area.id); @@ -358,12 +358,18 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un return false; return target->getGender() == gender.gender; case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT: - return sScriptMgr->OnCriteriaCheck(ScriptId, const_cast<Player*>(source), const_cast<Unit*>(target)); + return sScriptMgr->OnCriteriaCheck(ScriptId, const_cast<Player*>(source), const_cast<Unit*>(target), criteria_id); case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY: + { if (source->GetMap()->IsRaid()) if (source->GetMap()->Is25ManRaid() != ((difficulty.difficulty & RAID_DIFFICULTY_MASK_25MAN) != 0)) return false; - return source->GetMap()->GetSpawnMode() >= difficulty.difficulty; + + AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id); + uint8 spawnMode = source->GetMap()->GetSpawnMode(); + // Dungeons completed on heroic mode count towards both in general achievement, but not in statistics. + return sAchievementMgr->IsStatisticCriteria(criteria) ? spawnMode == difficulty.difficulty : spawnMode >= difficulty.difficulty; + } case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: return source->GetMap()->GetPlayersCountExceptGMs() <= map_players.maxcount; case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: @@ -1277,17 +1283,15 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui bool matchFound = false; for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) { - uint32 area_id = worldOverlayEntry->areatableID[j]; - if (!area_id) // array have 0 only in empty tail + AreaTableEntry const* area = sAreaTableStore.LookupEntry(worldOverlayEntry->areatableID[j]); + if (!area) break; - int32 exploreFlag = GetAreaFlagByAreaID(area_id); - if (exploreFlag < 0) + uint32 playerIndexOffset = uint32(area->exploreFlag) / 32; + if (playerIndexOffset >= PLAYER_EXPLORED_ZONES_SIZE) continue; - uint32 playerIndexOffset = uint32(exploreFlag) / 32; - uint32 mask = 1<< (uint32(exploreFlag) % 32); - + uint32 mask = 1 << (uint32(area->exploreFlag) % 32); if (GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) { matchFound = true; @@ -1661,8 +1665,14 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; } + case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA: // This also behaves like ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA { + // those requirements couldn't be found in the dbc + AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria); + if (!data || !data->Meets(GetPlayer(), NULL)) + continue; + // Check map id requirement if (miscValue1 == achievementCriteria->win_arena.mapID) SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); @@ -1678,7 +1688,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; // FIXME: not triggered in code as result, need to implement case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID: - case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK: case ACHIEVEMENT_CRITERIA_TYPE_TOTAL: break; // Not implemented yet :( @@ -1876,6 +1885,7 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED: case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN: case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS: + case ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA: default: break; } @@ -2163,9 +2173,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) } } - // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement - // TODO: where do set this instead? - if (!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) sAchievementMgr->SetRealmCompleted(achievement); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, achievement->ID); @@ -2307,6 +2315,61 @@ bool AchievementMgr::CanUpdateCriteria(AchievementCriteriaEntry const* criteria, return true; } +bool AchievementGlobalMgr::IsStatisticCriteria(AchievementCriteriaEntry const* achievementCriteria) const +{ + return isStatisticAchievement(sAchievementStore.LookupEntry(achievementCriteria->referredAchievement)); +} + +bool AchievementGlobalMgr::isStatisticAchievement(AchievementEntry const* achievement) const +{ + if (!achievement) + return false; + + AchievementCategoryEntry const* cat = sAchievementCategoryStore.LookupEntry(achievement->categoryId); + do { + switch(cat->ID) { + case ACHIEVEMENT_CATEGORY_STATISTICS: + return true; + case ACHIEVEMENT_CATEOGRY_GENERAL: + return false; + default: + cat = sAchievementCategoryStore.LookupEntry(cat->parentCategory); + break; + } + } while (cat); + + return false; +} + +bool AchievementGlobalMgr::IsRealmCompleted(AchievementEntry const* achievement) const +{ + auto itr = m_allCompletedAchievements.find(achievement->ID); + if (itr == m_allCompletedAchievements.end()) + return false; + + if (itr->second == std::chrono::system_clock::time_point::min()) + return false; + + if (itr->second == std::chrono::system_clock::time_point::max()) + return true; + + // Allow completing the realm first kill for entire minute after first person did it + // it may allow more than one group to achieve it (highly unlikely) + // but apparently this is how blizz handles it as well + if (achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL) + return (std::chrono::system_clock::now() - itr->second) > std::chrono::minutes(1); + + return true; +} + +void AchievementGlobalMgr::SetRealmCompleted(AchievementEntry const* achievement) +{ + if (IsRealmCompleted(achievement)) + return; + + m_allCompletedAchievements[achievement->ID] = std::chrono::system_clock::now(); +} + //========================================================== void AchievementGlobalMgr::LoadAchievementCriteriaList() { @@ -2628,6 +2691,14 @@ void AchievementGlobalMgr::LoadCompletedAchievements() QueryResult result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement"); + // Populate _allCompletedAchievements with all realm first achievement ids to make multithreaded access safer + // while it will not prevent races, it will prevent crashes that happen because std::unordered_map key was added + // instead the only potential race will happen on value associated with the key + for (uint32 i = 0; i < sAchievementStore.GetNumRows(); ++i) + if (AchievementEntry const* achievement = sAchievementStore.LookupEntry(i)) + if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) + m_allCompletedAchievements[achievement->ID] = std::chrono::system_clock::time_point::min(); + if (!result) { sLog->outString(">> Loaded 0 completed achievements. DB table `character_achievement` is empty."); @@ -2655,7 +2726,7 @@ void AchievementGlobalMgr::LoadCompletedAchievements() continue; } else if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) - m_allCompletedAchievements.insert(achievementId); + m_allCompletedAchievements[achievementId] = std::chrono::system_clock::time_point::max(); } while (result->NextRow()); sLog->outString(">> Loaded %lu completed achievements in %u ms", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime)); diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h index 390a0cb589..74fe0046ef 100644 --- a/src/server/game/Achievements/AchievementMgr.h +++ b/src/server/game/Achievements/AchievementMgr.h @@ -8,6 +8,7 @@ #include <map> #include <string> +#include <chrono> #include "Common.h" #include <ace/Singleton.h> @@ -55,9 +56,14 @@ enum AchievementCriteriaDataType ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY = 22, // N login on day of N-th Birthday ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE = 23 // title_id known (pvp) title, values from dbc }; - #define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 24 // maximum value in AchievementCriteriaDataType enum +enum AchievementCommonCategories +{ + ACHIEVEMENT_CATEOGRY_GENERAL = -1, + ACHIEVEMENT_CATEGORY_STATISTICS = 1 +}; + class Player; class Unit; @@ -294,6 +300,9 @@ class AchievementGlobalMgr ~AchievementGlobalMgr() {} public: + bool IsStatisticCriteria(AchievementCriteriaEntry const* achievementCriteria) const; + bool isStatisticAchievement(AchievementEntry const* achievement) const; + AchievementCriteriaEntryList const* GetAchievementCriteriaByType(AchievementCriteriaTypes type) const { return &m_AchievementCriteriasByType[type]; @@ -348,15 +357,8 @@ class AchievementGlobalMgr return iter != m_criteriaDataMap.end() ? &iter->second : NULL; } - bool IsRealmCompleted(AchievementEntry const* achievement) const - { - return m_allCompletedAchievements.find(achievement->ID) != m_allCompletedAchievements.end(); - } - - void SetRealmCompleted(AchievementEntry const* achievement) - { - m_allCompletedAchievements.insert(achievement->ID); - } + bool IsRealmCompleted(AchievementEntry const* achievement) const; + void SetRealmCompleted(AchievementEntry const* achievement); void LoadAchievementCriteriaList(); void LoadAchievementCriteriaData(); @@ -375,7 +377,7 @@ class AchievementGlobalMgr // store achievements by referenced achievement id to speed up lookup AchievementListByReferencedId m_AchievementListByReferencedId; - typedef std::set<uint32> AllCompletedAchievements; + typedef UNORDERED_MAP<uint32 /*achievementId*/, std::chrono::system_clock::time_point /*completionTime*/> AllCompletedAchievements; AllCompletedAchievements m_allCompletedAchievements; AchievementRewards m_achievementRewards; diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 144eb65465..e4ac0d3901 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -956,6 +956,8 @@ void Battleground::EndBattleground(TeamId winnerTeamId) // Arena lost => reset the win_rated_arena having the "no_lose" condition player->ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_CONDITION_NO_LOSE, 0); } + + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA, GetMapId()); } uint32 winner_kills = player->GetRandomWinner() ? BG_REWARD_WINNER_HONOR_LAST : BG_REWARD_WINNER_HONOR_FIRST; diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index 2d6aae341a..2b4f7c26ae 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -111,8 +111,8 @@ set(game_STAT_SRCS include_directories( ${game_INCLUDE_DIRS} ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Detour - ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Recast + ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Detour/Include + ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Recast/Include ${CMAKE_SOURCE_DIR}/deps/g3dlite/include ${CMAKE_SOURCE_DIR}/deps/SFMT ${CMAKE_SOURCE_DIR}/deps/zlib diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 9e94ec8ee3..79986a7538 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -301,11 +301,11 @@ bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, c uint32 areaId = player->GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; - if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) { int locale = GetSessionDbcLocale(); areaName = area->area_name[locale]; - if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone)) + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone)) zoneName = zone->area_name[locale]; } diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index ee334106c2..ccf36914cf 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1658,7 +1658,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) } case CONDITION_ZONEID: { - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->ConditionValue1); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(cond->ConditionValue1); if (!areaEntry) { sLog->outErrorDb("ZoneID condition has non existing area (%u), skipped", cond->ConditionValue1); diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index f9c2648116..d92d3f78a2 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -38,15 +38,14 @@ struct WMOAreaTableTripple typedef std::map<WMOAreaTableTripple, WMOAreaTableEntry const*> WMOAreaInfoByTripple; -DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt); +DBCStorage <AreaTableEntry> sAreaTableStore(AreaTableEntryfmt); DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt); DBCStorage <AreaPOIEntry> sAreaPOIStore(AreaPOIEntryfmt); -static AreaFlagByAreaID sAreaFlagByAreaID; -static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files static WMOAreaInfoByTripple sWMOAreaInfoByTripple; DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt); +DBCStorage <AchievementCategoryEntry> sAchievementCategoryStore(AchievementCategoryfmt); DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt); DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt); DBCStorage <AuctionHouseEntry> sAuctionHouseStore(AuctionHouseEntryfmt); @@ -100,7 +99,6 @@ DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptf DBCStorage <HolidaysEntry> sHolidaysStore(Holidaysfmt); -DBCStorage <ItemEntry> sItemStore(Itemfmt); DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore(ItemBagFamilyfmt); //DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); @@ -255,23 +253,10 @@ void LoadDBCStores(const std::string& dataPath) StoreProblemList bad_dbc_files; uint32 availableDbcLocales = 0xFFFFFFFF; - LoadDBC(availableDbcLocales, bad_dbc_files, sAreaStore, dbcPath, "AreaTable.dbc"); - - // must be after sAreaStore loading - for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0 - { - if (AreaTableEntry const* area = sAreaStore.LookupEntry(i)) - { - // fill AreaId->DBC records - sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID), area->exploreFlag)); - - // fill MapId->DBC records (skip sub zones and continents) - if (area->zone == 0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 && area->mapid != 571) - sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid, area->exploreFlag)); - } - } + LoadDBC(availableDbcLocales, bad_dbc_files, sAreaTableStore, dbcPath, "AreaTable.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementStore, dbcPath, "Achievement.dbc", &CustomAchievementfmt, &CustomAchievementIndex); + LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementCategoryStore, dbcPath, "Achievement_Category.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementCriteriaStore, dbcPath, "Achievement_Criteria.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sAreaTriggerStore, dbcPath, "AreaTrigger.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sAreaGroupStore, dbcPath, "AreaGroup.dbc"); @@ -346,7 +331,6 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sHolidaysStore, dbcPath, "Holidays.dbc"); - LoadDBC(availableDbcLocales, bad_dbc_files, sItemStore, dbcPath, "Item.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sItemBagFamilyStore, dbcPath, "ItemBagFamily.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sItemDisplayInfoStore, dbcPath, "ItemDisplayInfo.dbc"); //LoadDBC(dbcCount, availableDbcLocales, bad_dbc_files, sItemCondExtCostsStore, dbcPath, "ItemCondExtCosts.dbc"); @@ -626,10 +610,9 @@ void LoadDBCStores(const std::string& dataPath) } // Check loaded DBC files proper version - if (!sAreaStore.LookupEntry(3617) || // last area (areaflag) added in 3.3.5a + if (!sAreaTableStore.LookupEntry(4987) || // last area added in 3.3.5a !sCharTitlesStore.LookupEntry(177) || // last char title added in 3.3.5a !sGemPropertiesStore.LookupEntry(1629) || // last added spell in 3.3.5a - !sItemStore.LookupEntry(56806) || // last gem property added in 3.3.5a !sItemExtendedCostStore.LookupEntry(2997) || // last item extended cost added in 3.3.5a !sMapStore.LookupEntry(724) || // last map added in 3.3.5a !sSpellStore.LookupEntry(80864) ) // last client known item added in 3.3.5a @@ -678,50 +661,12 @@ uint32 GetTalentSpellCost(uint32 spellId) return 0; } -int32 GetAreaFlagByAreaID(uint32 area_id) -{ - AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); - if (i == sAreaFlagByAreaID.end()) - return -1; - - return i->second; -} - WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid) { WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid)); - if (i == sWMOAreaInfoByTripple.end()) - return NULL; - return i->second; -} - -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) -{ - int32 areaflag = GetAreaFlagByAreaID(area_id); - if (areaflag < 0) + if (i == sWMOAreaInfoByTripple.end()) return NULL; - - return sAreaStore.LookupEntry(areaflag); -} - -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id) -{ - if (area_flag) - return sAreaStore.LookupEntry(area_flag); - - if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) - return GetAreaEntryByAreaID(mapEntry->linked_zone); - - return NULL; -} - -uint32 GetAreaFlagByMapId(uint32 mapid) -{ - AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); - if (i == sAreaFlagByMapID.end()) - return 0; - else - return i->second; + return i->second; } uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index d69d2132b9..5b241f6fcb 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -20,11 +20,6 @@ char* GetPetName(uint32 petfamily, uint32 dbclang); uint32 GetTalentSpellCost(uint32 spellId); TalentSpellPos const* GetTalentSpellPos(uint32 spellId); -int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id); -uint32 GetAreaFlagByMapId(uint32 mapid); - WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid); uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); @@ -58,7 +53,8 @@ uint32 GetDefaultMapLight(uint32 mapId); extern DBCStorage <AchievementEntry> sAchievementStore; extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore; -extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions +extern DBCStorage <AchievementCategoryEntry> sAchievementCategoryStore; +extern DBCStorage <AreaTableEntry> sAreaTableStore; extern DBCStorage <AreaGroupEntry> sAreaGroupStore; extern DBCStorage <AreaPOIEntry> sAreaPOIStore; extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore; @@ -104,7 +100,6 @@ extern DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore; extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore; extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore; extern DBCStorage <HolidaysEntry> sHolidaysStore; -extern DBCStorage <ItemEntry> sItemStore; extern DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore; extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore; extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 3b7ecee547..e43351b9dd 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -46,7 +46,7 @@ struct AchievementEntry struct AchievementCategoryEntry { uint32 ID; // 0 - uint32 parentCategory; // 1 -1 for main category + int32 parentCategory; // 1 -1 for main category //char *name[16]; // 2-17 //uint32 name_flags; // 18 //uint32 sortOrder; // 19 @@ -1092,18 +1092,6 @@ struct HolidaysEntry //uint32 flags; // 54 m_flags (0 = Darkmoon Faire, Fishing Contest and Wotlk Launch, rest is 1) }; -struct ItemEntry -{ - uint32 ID; // 0 - uint32 Class; // 1 - uint32 SubClass; // 2 some items have strange subclasses - int32 SoundOverrideSubclass; // 3 - int32 Material; // 4 - uint32 DisplayId; // 5 - uint32 InventoryType; // 6 - uint32 Sheath; // 7 -}; - struct ItemBagFamilyEntry { uint32 ID; // 0 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 395de0fe9d..8e1a6c3395 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -10,8 +10,9 @@ char const Achievementfmt[] = "niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; const std::string CustomAchievementfmt="pppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapapaaaaaaaaaaaaaaaaaapp"; const std::string CustomAchievementIndex = "ID"; +char const AchievementCategoryfmt[] = "nixxxxxxxxxxxxxxxxxx"; char const AchievementCriteriafmt[] = "niiiiiiiixxxxxxxxxxxxxxxxxiiiix"; -char const AreaTableEntryfmt[] = "iiinixxxxxissssssssssssssssxiiiiixxx"; +char const AreaTableEntryfmt[] = "niiiixxxxxissssssssssssssssxiiiiixxx"; char const AreaGroupEntryfmt[] = "niiiiiii"; char const AreaPOIEntryfmt[] = "niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; char const AreaTriggerEntryfmt[] = "niffffffff"; @@ -57,7 +58,6 @@ char const GtOCTRegenHPfmt[] = "f"; char const GtRegenHPPerSptfmt[] = "f"; char const GtRegenMPPerSptfmt[] = "f"; char const Holidaysfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix"; -char const Itemfmt[] = "niiiiiii"; char const ItemBagFamilyfmt[] = "nxxxxxxxxxxxxxxxxx"; char const ItemDisplayTemplateEntryfmt[] = "nxxxxsxxxxxxxxxxxxxxxxxxx"; //char const ItemCondExtCostsEntryfmt[] = "xiii"; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 176315d742..8a2a0f3a13 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -353,7 +353,7 @@ bool Creature::InitEntry(uint32 Entry, const CreatureData* data) // Load creature equipment if (!data || data->equipmentId == 0) // use default from the template - LoadEquipment(GetCreatureData() ? GetOriginalEquipmentId() : 1); + LoadEquipment(); else if (data && data->equipmentId != 0) // override, 0 means no equipment { m_originalEquipmentId = data->equipmentId; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index f3091758f6..d1731510ea 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1095,9 +1095,13 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const if (!IsInMap(obj)) return false; - float ox, oy, oz; - obj->GetPosition(ox, oy, oz); - return IsWithinLOS(ox, oy, oz); + float x, y, z; + if (obj->GetTypeId() == TYPEID_PLAYER) + obj->GetPosition(x, y, z); + else + obj->GetHitSpherePointFor(GetPosition(), x, y, z); + + return IsWithinLOS(x, y, z); } bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const @@ -1107,11 +1111,36 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);*/ if (IsInWorld()) - return GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ()+2.f, ox, oy, oz+2.f, GetPhaseMask()); + { + float x, y, z; + if (GetTypeId() == TYPEID_PLAYER) + GetPosition(x, y, z); + else + GetHitSpherePointFor({ ox, oy, oz }, x, y, z); + + return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask()); + } return true; } +Position WorldObject::GetHitSpherePointFor(Position const& dest) const +{ + G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ()); + G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); + G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * std::min(dest.GetExactDist(this), GetObjectSize()); + + return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y)); +} + +void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const +{ + Position pos = GetHitSpherePointFor(dest); + x = pos.GetPositionX(); + y = pos.GetPositionY(); + z = pos.GetPositionZ(); +} + bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D /* = true */) const { float dx1 = GetPositionX() - obj1->GetPositionX(); @@ -2402,123 +2431,33 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, else UpdateAllowedPositionZ(x, y, z); - /* // if detection disabled, return first point - if (!sWorld->getIntConfig(CONFIG_DETECT_POS_COLLISION)) - { - UpdateGroundPositionZ(x, y, z); // update to LOS height if available + if (!sWorld->getBoolConfig(CONFIG_DETECT_POS_COLLISION)) return; - } - // or remember first point - float first_x = x; - float first_y = y; - bool first_los_conflict = false; // first point LOS problems - - // prepare selector for work - ObjectPosSelector selector(GetPositionX(), GetPositionY(), GetObjectSize(), distance2d+searcher_size); - - // adding used positions around object - { - CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.SetNoCreate(); - - Trinity::NearUsedPosDo u_do(*this, searcher, absAngle, selector); - Trinity::WorldObjectWorker<Trinity::NearUsedPosDo> worker(this, u_do); - - TypeContainerVisitor<Trinity::WorldObjectWorker<Trinity::NearUsedPosDo>, GridTypeMapContainer > grid_obj_worker(worker); - TypeContainerVisitor<Trinity::WorldObjectWorker<Trinity::NearUsedPosDo>, WorldTypeMapContainer > world_obj_worker(worker); - - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, grid_obj_worker, *GetMap(), *this, distance2d); - cell_lock->Visit(cell_lock, world_obj_worker, *GetMap(), *this, distance2d); - } - - // maybe can just place in primary position - if (selector.CheckOriginal()) - { - UpdateGroundPositionZ(x, y, z); // update to LOS height if available - - if (IsWithinLOS(x, y, z)) - return; - - first_los_conflict = true; // first point have LOS problems - } - - float angle; // candidate of angle for free pos - - // special case when one from list empty and then empty side preferred - if (selector.FirstAngle(angle)) - { - GetNearPoint2D(x, y, distance2d, absAngle+angle); - z = GetPositionZ(); - UpdateGroundPositionZ(x, y, z); // update to LOS height if available - - if (IsWithinLOS(x, y, z)) - return; - } - - // set first used pos in lists - selector.InitializeAngle(); - - // select in positions after current nodes (selection one by one) - while (selector.NextAngle(angle)) // angle for free pos - { - GetNearPoint2D(x, y, distance2d, absAngle+angle); - z = GetPositionZ(); - UpdateGroundPositionZ(x, y, z); // update to LOS height if available - - if (IsWithinLOS(x, y, z)) - return; - } - - // BAD NEWS: not free pos (or used or have LOS problems) - // Attempt find _used_ pos without LOS problem - - if (!first_los_conflict) - { - x = first_x; - y = first_y; - - UpdateGroundPositionZ(x, y, z); // update to LOS height if available + // return if the point is already in LoS + if (IsWithinLOS(x, y, z)) return; - } - - // special case when one from list empty and then empty side preferred - if (selector.IsNonBalanced()) - { - if (!selector.FirstAngle(angle)) // _used_ pos - { - GetNearPoint2D(x, y, distance2d, absAngle+angle); - z = GetPositionZ(); - UpdateGroundPositionZ(x, y, z); // update to LOS height if available - if (IsWithinLOS(x, y, z)) - return; - } - } - - // set first used pos in lists - selector.InitializeAngle(); + // remember first point + float first_x = x; + float first_y = y; + float first_z = z; - // select in positions after current nodes (selection one by one) - while (selector.NextUsedAngle(angle)) // angle for used pos but maybe without LOS problem + // loop in a circle to look for a point in LoS using small steps + for (float angle = float(M_PI) / 8; angle < float(M_PI) * 2; angle += float(M_PI) / 8) { - GetNearPoint2D(x, y, distance2d, absAngle+angle); + GetNearPoint2D(x, y, distance2d + searcher_size, absAngle + angle); z = GetPositionZ(); - UpdateGroundPositionZ(x, y, z); // update to LOS height if available - + UpdateAllowedPositionZ(x, y, z); if (IsWithinLOS(x, y, z)) return; } - // BAD BAD NEWS: all found pos (free and used) have LOS problem :( + // still not in LoS, give up and return first position found x = first_x; y = first_y; - - UpdateGroundPositionZ(x, y, z); // update to LOS height if available - */ + z = first_z; } bool WorldObject::GetClosePoint(float &x, float &y, float &z, float size, float distance2d, float angle, const WorldObject* forWho, bool force) const diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 123b1aa08e..68b8bda28c 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -365,6 +365,17 @@ class Object struct Position { + Position(float x = 0, float y = 0, float z = 0, float o = 0) + : m_positionX(x), m_positionY(y), m_positionZ(z), m_orientation(NormalizeOrientation(o)) { } + + Position(Position const& loc) { Relocate(loc); } + + struct PositionXYStreamer + { + explicit PositionXYStreamer(Position& pos) : Pos(&pos) { } + Position* Pos; + }; + struct PositionXYZStreamer { explicit PositionXYZStreamer(Position& pos) : m_pos(&pos) {} @@ -382,19 +393,38 @@ struct Position float m_positionZ; float m_orientation; + bool operator==(Position const &a); + + inline bool operator!=(Position const &a) + { + return !(operator==(a)); + } + void Relocate(float x, float y) - { m_positionX = x; m_positionY = y;} + { + m_positionX = x; m_positionY = y; + } void Relocate(float x, float y, float z) - { m_positionX = x; m_positionY = y; m_positionZ = z; } + { + m_positionX = x; m_positionY = y; m_positionZ = z; + } void Relocate(float x, float y, float z, float orientation) - { m_positionX = x; m_positionY = y; m_positionZ = z; m_orientation = orientation; } + { + m_positionX = x; m_positionY = y; m_positionZ = z; m_orientation = orientation; + } void Relocate(const Position &pos) - { m_positionX = pos.m_positionX; m_positionY = pos.m_positionY; m_positionZ = pos.m_positionZ; m_orientation = pos.m_orientation; } + { + m_positionX = pos.m_positionX; m_positionY = pos.m_positionY; m_positionZ = pos.m_positionZ; m_orientation = pos.m_orientation; + } void Relocate(const Position* pos) - { m_positionX = pos->m_positionX; m_positionY = pos->m_positionY; m_positionZ = pos->m_positionZ; m_orientation = pos->m_orientation; } + { + m_positionX = pos->m_positionX; m_positionY = pos->m_positionY; m_positionZ = pos->m_positionZ; m_orientation = pos->m_orientation; + } void RelocateOffset(const Position &offset); void SetOrientation(float orientation) - { m_orientation = orientation; } + { + m_orientation = orientation; + } float GetPositionX() const { return m_positionX; } float GetPositionY() const { return m_positionY; } @@ -402,17 +432,26 @@ struct Position float GetOrientation() const { return m_orientation; } void GetPosition(float &x, float &y) const - { x = m_positionX; y = m_positionY; } + { + x = m_positionX; y = m_positionY; + } void GetPosition(float &x, float &y, float &z) const - { x = m_positionX; y = m_positionY; z = m_positionZ; } + { + x = m_positionX; y = m_positionY; z = m_positionZ; + } void GetPosition(float &x, float &y, float &z, float &o) const - { x = m_positionX; y = m_positionY; z = m_positionZ; o = m_orientation; } + { + x = m_positionX; y = m_positionY; z = m_positionZ; o = m_orientation; + } void GetPosition(Position* pos) const { if (pos) pos->Relocate(m_positionX, m_positionY, m_positionZ, m_orientation); } + Position GetPosition() const { return *this; } + + Position::PositionXYZStreamer PositionXYZStream() { return PositionXYZStreamer(*this); @@ -425,39 +464,65 @@ struct Position bool IsPositionValid() const; float GetExactDist2dSq(float x, float y) const - { float dx = m_positionX - x; float dy = m_positionY - y; return dx*dx + dy*dy; } + { + float dx = m_positionX - x; float dy = m_positionY - y; return dx*dx + dy*dy; + } float GetExactDist2d(const float x, const float y) const - { return sqrt(GetExactDist2dSq(x, y)); } + { + return sqrt(GetExactDist2dSq(x, y)); + } float GetExactDist2dSq(const Position* pos) const - { float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; return dx*dx + dy*dy; } + { + float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; return dx*dx + dy*dy; + } float GetExactDist2d(const Position* pos) const - { return sqrt(GetExactDist2dSq(pos)); } + { + return sqrt(GetExactDist2dSq(pos)); + } float GetExactDistSq(float x, float y, float z) const - { float dz = m_positionZ - z; return GetExactDist2dSq(x, y) + dz*dz; } + { + float dz = m_positionZ - z; return GetExactDist2dSq(x, y) + dz*dz; + } float GetExactDist(float x, float y, float z) const - { return sqrt(GetExactDistSq(x, y, z)); } + { + return sqrt(GetExactDistSq(x, y, z)); + } float GetExactDistSq(const Position* pos) const - { float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; float dz = m_positionZ - pos->m_positionZ; return dx*dx + dy*dy + dz*dz; } + { + float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; float dz = m_positionZ - pos->m_positionZ; return dx*dx + dy*dy + dz*dz; + } float GetExactDist(const Position* pos) const - { return sqrt(GetExactDistSq(pos)); } + { + return sqrt(GetExactDistSq(pos)); + } void GetPositionOffsetTo(const Position & endPos, Position & retOffset) const; float GetAngle(const Position* pos) const; float GetAngle(float x, float y) const; float GetRelativeAngle(const Position* pos) const - { return GetAngle(pos) - m_orientation; } + { + return GetAngle(pos) - m_orientation; + } float GetRelativeAngle(float x, float y) const { return GetAngle(x, y) - m_orientation; } void GetSinCos(float x, float y, float &vsin, float &vcos) const; bool IsInDist2d(float x, float y, float dist) const - { return GetExactDist2dSq(x, y) < dist * dist; } + { + return GetExactDist2dSq(x, y) < dist * dist; + } bool IsInDist2d(const Position* pos, float dist) const - { return GetExactDist2dSq(pos) < dist * dist; } + { + return GetExactDist2dSq(pos) < dist * dist; + } bool IsInDist(float x, float y, float z, float dist) const - { return GetExactDistSq(x, y, z) < dist * dist; } + { + return GetExactDistSq(x, y, z) < dist * dist; + } bool IsInDist(const Position* pos, float dist) const - { return GetExactDistSq(pos) < dist * dist; } + { + return GetExactDistSq(pos) < dist * dist; + } bool IsWithinBox(const Position& center, float xradius, float yradius, float zradius) const; bool HasInArc(float arcangle, const Position* pos, float targetRadius = 0.0f) const; @@ -479,10 +544,13 @@ struct Position return fmod(o, 2.0f * static_cast<float>(M_PI)); } }; -ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer); +ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYStreamer const& streamer); +ByteBuffer& operator >> (ByteBuffer& buf, Position::PositionXYStreamer const& streamer); ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer); -ByteBuffer& operator>>(ByteBuffer& buf, Position::PositionXYZStreamer const& streamer); +ByteBuffer& operator >> (ByteBuffer& buf, Position::PositionXYZStreamer const& streamer); ByteBuffer& operator<<(ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer); +ByteBuffer& operator >> (ByteBuffer& buf, Position::PositionXYZOStreamer const& streamer); + struct MovementInfo { @@ -785,6 +853,8 @@ class WorldObject : public Object, public WorldLocation } bool IsWithinLOS(float x, float y, float z) const; bool IsWithinLOSInMap(const WorldObject* obj) const; + Position GetHitSpherePointFor(Position const& dest) const; + void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const; bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const; bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const; bool IsInRange2d(float x, float y, float minRange, float maxRange) const; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 4d064f5afc..e887d6cb60 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -960,6 +960,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel) SetCreateHealth(4 * petlevel); SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - 30 - (petlevel / 4) + m_owner->GetTotalAttackPowerValue(BASE_ATTACK) * 0.006f)); SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel - 30 + (petlevel / 4) + m_owner->GetTotalAttackPowerValue(BASE_ATTACK) * 0.006f)); + SetReactState(REACT_DEFENSIVE); break; } case NPC_ARMY_OF_THE_DEAD: diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 41c4f3a1af..ece890078a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2189,8 +2189,8 @@ void Player::SendTeleportAckPacket() GetSession()->SendPacket(&data); } -bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options) -{ +bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options /*= 0*/, Unit *target /*= nullptr*/) +{ if (!MapManager::IsValidMapCoord(mapid, x, y, z, orientation)) { sLog->outError("TeleportTo: invalid map (%d) or invalid coordinates (X: %f, Y: %f, Z: %f, O: %f) given when teleporting player (GUID: %u, name: %s, map: %d, X: %f, Y: %f, Z: %f, O: %f).", @@ -2288,6 +2288,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati if (duel && GetMapId() != mapid && GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER))) DuelComplete(DUEL_FLED); + if (!sScriptMgr->OnBeforePlayerTeleport(this, mapid, x, y, z, orientation, options, target)) + return false; + if (GetMapId() == mapid) { //lets reset far teleport flag if it wasn't reset during chained teleports @@ -3026,11 +3029,7 @@ void Player::SetGameMaster(bool on) else { // restore phase - uint32 newPhase = 0; - AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE); - if (!phases.empty()) - for (AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) - newPhase |= (*itr)->GetMiscValue(); + uint32 newPhase = GetPhaseByAuras(); if (!newPhase) newPhase = PHASEMASK_NORMAL; @@ -3063,14 +3062,21 @@ void Player::SetGameMaster(bool on) } void Player::SetGMVisible(bool on) -{ +{ + const uint32 VISUAL_AURA = 37800; + if (on) { + if (HasAura(VISUAL_AURA, 0)) + RemoveAurasDueToSpell(VISUAL_AURA); + m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; //remove flag m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); } else { + AddAura(VISUAL_AURA, this); + m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE; //add flag SetAcceptWhispers(false); @@ -5286,7 +5292,7 @@ void Player::CreateCorpse() return; } - _uf = GetUInt32Value(UNIT_FIELD_BYTES_0); + _uf = getRace(); _pb = GetUInt32Value(PLAYER_BYTES); _pb2 = GetUInt32Value(PLAYER_BYTES_2); @@ -5577,11 +5583,11 @@ void Player::RepopAtGraveyard() // note: this can be called also when the player is alive // for example from WorldSession::HandleMovementOpcodes - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetAreaId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetAreaId()); // Such zones are considered unreachable as a ghost and the player must be automatically revived // Xinef: Get Transport Check is not needed - if ((!IsAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) /*|| GetTransport()*/ || GetPositionZ() < -500.0f) + if ((!IsAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) /*|| GetTransport()*/ || GetPositionZ() < GetMap()->GetMinHeight(GetPositionX(), GetPositionY())) { ResurrectPlayer(0.5f); SpawnCorpseBones(); @@ -5618,8 +5624,10 @@ void Player::RepopAtGraveyard() GetSession()->SendPacket(&data); } } - else if (GetPositionZ() < -500.0f) + else if (GetPositionZ() < GetMap()->GetMinHeight(GetPositionX(), GetPositionY())) TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation()); + + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); } bool Player::CanJoinConstantChannelInZone(ChatChannelsEntry const* channel, AreaTableEntry const* zone) @@ -5671,7 +5679,7 @@ void Player::UpdateLocalChannels(uint32 newZone) if (GetSession()->PlayerLoading() && !IsBeingTeleportedFar()) return; // The client handles it automatically after loading, but not after teleporting - AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone); + AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(newZone); if (!current_zone) return; @@ -6894,45 +6902,46 @@ void Player::CheckAreaExploreAndOutdoor() return; bool isOutdoor = IsOutdoors(); - uint32 areaFlag = GetAreaFlagByAreaID(GetAreaId()); - + uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); + if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor) RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY); - if (areaFlag == 0xffff) + if (!areaId) return; - int offset = areaFlag / 32; + + if (!areaEntry) + { + sLog->outError("Player '%s' (%u) discovered unknown area (x: %f y: %f z: %f map: %u)", + GetName().c_str(), GetGUIDLow(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId()); + return; + } + + uint32 offset = areaEntry->exploreFlag / 32; if (offset >= PLAYER_EXPLORED_ZONES_SIZE) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); + sLog->outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaEntry->flags, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); #endif return; } - uint32 val = (uint32)(1 << (areaFlag % 32)); + uint32 val = (uint32)(1 << (areaEntry->exploreFlag % 32)); uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); if (!(currFields & val)) { SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, (uint32)(currFields | val)); - UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA, GetAreaId()); - - AreaTableEntry const* areaEntry = GetAreaEntryByAreaFlagAndMap(areaFlag, GetMapId()); - if (!areaEntry) - { - sLog->outError("Player %u discovered unknown area (x: %f y: %f z: %f map: %u", GetGUIDLow(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId()); - return; - } + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA, areaId); if (areaEntry->area_level > 0) { - uint32 area = areaEntry->ID; if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { - SendExplorationExperience(area, 0); + SendExplorationExperience(areaId, 0); } else { @@ -6958,10 +6967,10 @@ void Player::CheckAreaExploreAndOutdoor() } GiveXP(XP, NULL); - SendExplorationExperience(area, XP); + SendExplorationExperience(areaId, XP); } #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) - sLog->outDetail("Player %u discovered a new area: %u", GetGUIDLow(), area); + sLog->outDetail("Player %u discovered a new area: %u", GetGUIDLow(), areaId); #endif } } @@ -7528,12 +7537,14 @@ void Player::UpdateArea(uint32 newArea) // pussywizard: inform instance, needed for Icecrown Citadel if (InstanceScript* instance = GetInstanceScript()) instance->OnPlayerAreaUpdate(this, m_areaUpdateId, newArea); + + sScriptMgr->OnPlayerUpdateArea(this, m_areaUpdateId, newArea); // FFA_PVP flags are area and not zone id dependent // so apply them accordingly m_areaUpdateId = newArea; - AreaTableEntry const* area = GetAreaEntryByAreaID(newArea); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(newArea); bool oldFFAPvPArea = pvpInfo.IsInFFAPvPArea; pvpInfo.IsInFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA); UpdatePvPState(true); @@ -7568,7 +7579,7 @@ void Player::UpdateArea(uint32 newArea) } // Xinef: area should inherit zone flags - AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone); uint32 areaFlags = area->flags; bool isSanctuary = area->IsSanctuary(); bool isInn = area->IsInn(GetTeamId(true)); @@ -7656,7 +7667,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) // zone changed, so area changed as well, update it UpdateArea(newArea); - AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(newZone); if (!zone) return; @@ -9258,6 +9269,12 @@ void Player::SendLoot(uint64 guid, LootType loot_type) loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true); permission = OWNER_PERMISSION; + //Inform instance if creature is skinned. + if (InstanceScript* mapInstance = creature->GetInstanceScript()) + { + mapInstance->CreatureLooted(creature, LOOT_SKINNING); + } + // Xinef: Set new loot recipient creature->SetLootRecipient(this, false); } @@ -20736,7 +20753,7 @@ void Player::Say(const std::string& text, const uint32 language) sScriptMgr->OnPlayerChat(this, CHAT_MSG_SAY, language, _text); WorldPacket data; - ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, Language(language), this, this, text); + ChatHandler::BuildChatPacket(data, CHAT_MSG_SAY, Language(language), this, this, _text); SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true); } @@ -20746,7 +20763,7 @@ void Player::Yell(const std::string& text, const uint32 language) sScriptMgr->OnPlayerChat(this, CHAT_MSG_YELL, language, _text); WorldPacket data; - ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, Language(language), this, this, text); + ChatHandler::BuildChatPacket(data, CHAT_MSG_YELL, Language(language), this, this, _text); SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true); } @@ -20756,7 +20773,7 @@ void Player::TextEmote(const std::string& text) sScriptMgr->OnPlayerChat(this, CHAT_MSG_EMOTE, LANG_UNIVERSAL, _text); WorldPacket data; - ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, text); + ChatHandler::BuildChatPacket(data, CHAT_MSG_EMOTE, LANG_UNIVERSAL, this, this, _text); SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)); } @@ -20773,14 +20790,14 @@ void Player::Whisper(const std::string& text, uint32 language, uint64 receiver) sScriptMgr->OnPlayerChat(this, CHAT_MSG_WHISPER, language, _text, rPlayer); WorldPacket data; - ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, Language(language), this, this, text); + ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, Language(language), this, this, _text); rPlayer->GetSession()->SendPacket(&data); // rest stuff shouldn't happen in case of addon message if (isAddonMessage) return; - ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER_INFORM, Language(language), rPlayer, rPlayer, text); + ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER_INFORM, Language(language), rPlayer, rPlayer, _text); GetSession()->SendPacket(&data); if (!isAcceptWhispers() && !IsGameMaster() && !rPlayer->IsGameMaster()) @@ -22658,19 +22675,30 @@ bool Player::IsVisibleGloballyFor(Player const* u) const } template<class T> -inline void UpdateVisibilityOf_helper(T* /*target*/, std::vector<Unit*>& /*v*/) +inline void UpdateVisibilityOf_helper(Player::ClientGUIDs& s64, T* target, std::vector<Unit*>& /*v*/) +{ + s64.insert(target->GetGUID()); +} + +template<> +inline void UpdateVisibilityOf_helper(Player::ClientGUIDs& s64, GameObject* target, std::vector<Unit*>& /*v*/) { + // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it + if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) + s64.insert(target->GetGUID()); } template<> -inline void UpdateVisibilityOf_helper(Creature* target, std::vector<Unit*>& v) +inline void UpdateVisibilityOf_helper(Player::ClientGUIDs& s64, Creature* target, std::vector<Unit*>& v) { + s64.insert(target->GetGUID()); v.push_back(target); } template<> -inline void UpdateVisibilityOf_helper(Player* target, std::vector<Unit*>& v) +inline void UpdateVisibilityOf_helper(Player::ClientGUIDs& s64, Player* target, std::vector<Unit*>& v) { + s64.insert(target->GetGUID()); v.push_back(target); } @@ -22784,9 +22812,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::vector<Unit*>& if (CanSeeOrDetect(target, false, true)) { target->BuildCreateUpdateBlockForPlayer(&data, this); - m_clientGUIDs.insert(target->GetGUID()); - - UpdateVisibilityOf_helper(target, visibleNow); + UpdateVisibilityOf_helper(m_clientGUIDs, target, visibleNow); } } } @@ -23610,8 +23636,8 @@ void Player::UpdateForQuestWorldObjects() GetSession()->SendPacket(&packet); } -void Player::SummonIfPossible(bool agree) -{ +void Player::SummonIfPossible(bool agree, uint32 summoner_guid) +{ if (!agree) { m_summon_expire = 0; @@ -23631,7 +23657,7 @@ void Player::SummonIfPossible(bool agree) UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1); - TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation()); + TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation(), 0, ObjectAccessor::FindPlayer(summoner_guid)); } void Player::RemoveItemDurations(Item* item) @@ -25226,22 +25252,19 @@ void Player::_LoadSkills(PreparedQueryResult result) } uint32 Player::GetPhaseMaskForSpawn() const -{ - uint32 phase = PHASEMASK_NORMAL; - if (!IsGameMaster()) - phase = GetPhaseMask(); - else - { - AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE); - if (!phases.empty()) - phase = phases.front()->GetMiscValue(); - } +{ + uint32 phase = IsGameMaster() ? GetPhaseByAuras() : GetPhaseMask(); + + if (!phase) + phase = PHASEMASK_NORMAL; + // some aura phases include 1 normal map in addition to phase itself - if (uint32 n_phase = phase & ~PHASEMASK_NORMAL) + uint32 n_phase = phase & ~PHASEMASK_NORMAL; + if (n_phase > 0) return n_phase; - return PHASEMASK_NORMAL; + return phase; } InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index a0f3ec042d..3e879bf1e6 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1096,10 +1096,10 @@ class Player : public Unit, public GridObject<Player> SetFloatValue(UNIT_FIELD_COMBATREACH, scale * DEFAULT_COMBAT_REACH); } - bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0); - bool TeleportTo(WorldLocation const &loc, uint32 options = 0) + bool TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options = 0, Unit *target = nullptr); + bool TeleportTo(WorldLocation const &loc, uint32 options = 0, Unit *target = nullptr) { - return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options); + return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options, target); } bool TeleportToEntryPoint(); @@ -1114,7 +1114,7 @@ class Player : public Unit, public GridObject<Player> } bool IsSummonAsSpectator() const { return m_summon_asSpectator && m_summon_expire >= time(NULL); } void SetSummonAsSpectator(bool on) { m_summon_asSpectator = on; } - void SummonIfPossible(bool agree); + void SummonIfPossible(bool agree, uint32 summoner_guid); time_t GetSummonExpireTimer() const { return m_summon_expire; } bool Create(uint32 guidlow, CharacterCreateInfo* createInfo); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 5166c71504..d0bcf5d290 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3395,6 +3395,15 @@ int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const return 0; } +bool Unit::CanMoveDuringChannel() const +{ + if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) + if (spell->getState() != SPELL_STATE_FINISHED) + return spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) && spell->IsChannelActive(); + + return false; +} + bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const { return IsWithinDistInMap(target, distance) && HasInArc(arc, target); @@ -13690,7 +13699,7 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* if (!spellInfo || castTime < 0) return; - if (spellInfo->IsChanneled() && !(spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)) + if (spellInfo->IsChanneled() && spellInfo->HasAura(SPELL_AURA_MOUNTED)) return; // called from caster @@ -14493,7 +14502,7 @@ void CharmInfo::InitPossessCreateSpells() { uint32 spellId = _unit->ToCreature()->m_spells[i]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (spellInfo && !spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) + if (spellInfo) { if (spellInfo->IsPassive()) _unit->CastSpell(_unit, spellInfo, true); @@ -14523,7 +14532,7 @@ void CharmInfo::InitCharmCreateSpells() uint32 spellId = _unit->ToCreature()->m_spells[x]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); - if (!spellInfo || spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD)) + if (!spellInfo) { _charmspells[x].SetActionAndType(spellId, ACT_DISABLED); continue; @@ -17547,6 +17556,17 @@ float Unit::MeleeSpellMissChance(const Unit* victim, WeaponAttackType attType, i return missChance; } +uint32 Unit::GetPhaseByAuras() const +{ + uint32 currentPhase = 0; + AuraEffectList const& phases = GetAuraEffectsByType(SPELL_AURA_PHASE); + if (!phases.empty()) + for (AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) + currentPhase |= (*itr)->GetMiscValue(); + + return currentPhase; +} + void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) { if (newPhaseMask == GetPhaseMask()) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a390a6359d..bb8dca9134 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2048,6 +2048,9 @@ class Unit : public WorldObject // delayed+channeled spells are always interrupted void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0, bool withInstant = true, bool bySelf = false); + // Check if our current channel spell has attribute SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING + bool CanMoveDuringChannel() const; + Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; } Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; } Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; @@ -2122,6 +2125,7 @@ class Unit : public WorldObject void SetModelVisible(bool on); // common function for visibility checks for player/creatures with detection code + uint32 GetPhaseByAuras() const; void SetPhaseMask(uint32 newPhaseMask, bool update);// overwrite WorldObject::SetPhaseMask void UpdateObjectVisibility(bool forced = true, bool fromUpdate = false); diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index 096825803b..26670a333a 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -19,6 +19,8 @@ #include "GameObjectAI.h" #include "Transport.h" +#include <time.h> + bool GameEventMgr::CheckOneGameEvent(uint16 entry) const { switch (mGameEvent[entry].state) @@ -194,8 +196,8 @@ void GameEventMgr::LoadFromDB() { { uint32 oldMSTime = getMSTime(); - // 1 2 3 4 5 6 7 8 - QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, description, world_event FROM game_event"); + // 1 2 3 4 5 6 7 8 9 + QueryResult result = WorldDatabase.Query("SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event FROM game_event"); if (!result) { mGameEvent.clear(); @@ -225,9 +227,13 @@ void GameEventMgr::LoadFromDB() pGameEvent.length = fields[4].GetUInt64(); pGameEvent.holiday_id = HolidayIds(fields[5].GetUInt32()); - pGameEvent.state = (GameEventState)(fields[7].GetUInt8()); + pGameEvent.holidayStage = fields[6].GetUInt8(); + pGameEvent.description = fields[7].GetString(); + pGameEvent.state = (GameEventState)(fields[8].GetUInt8()); pGameEvent.nextstart = 0; + ++count; + if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check { sLog->outErrorDb("`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.", event_id); @@ -241,11 +247,10 @@ void GameEventMgr::LoadFromDB() sLog->outErrorDb("`game_event` game event id (%i) have not existed holiday id %u.", event_id, pGameEvent.holiday_id); pGameEvent.holiday_id = HOLIDAY_NONE; } - } - pGameEvent.description = fields[6].GetString(); + SetHolidayEventTime(pGameEvent); + } - ++count; } while (result->NextRow()); @@ -951,6 +956,45 @@ void GameEventMgr::LoadFromDB() } } +void GameEventMgr::LoadHolidayDates() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT id, date_id, date_value FROM holiday_dates"); + + if (!result) + { + sLog->outString(">> Loaded 0 holiday dates. DB table `holiday_dates` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + uint32 holidayId = fields[0].GetUInt32(); + HolidaysEntry* entry = const_cast<HolidaysEntry*>(sHolidaysStore.LookupEntry(holidayId)); + if (!entry) + { + sLog->outErrorDb("holiday_dates entry has invalid holiday id %u.", holidayId); + continue; + } + uint8 dateId = fields[1].GetUInt8(); + if (dateId >= MAX_HOLIDAY_DATES) + { + sLog->outErrorDb("holiday_dates entry has out of range date_id %u.", dateId); + continue; + } + entry->Date[dateId] = fields[2].GetUInt32(); + modifiedHolidays.insert(entry->Id); + ++count; + + } while (result->NextRow()); + + sLog->outString(">> Loaded %u holiday dates in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + uint32 GameEventMgr::GetNPCFlag(Creature* cr) { uint32 mask = 0; @@ -1669,6 +1713,83 @@ void GameEventMgr::RunSmartAIScripts(uint16 event_id, bool activate) } } +void GameEventMgr::SetHolidayEventTime(GameEventData& event) +{ + if (!event.holidayStage) // Ignore holiday + return; + + const HolidaysEntry* holiday = sHolidaysStore.LookupEntry(event.holiday_id); + + if (!holiday->Date[0] || !holiday->Duration[0]) // Invalid definitions + { + sLog->outErrorDb("Missing date or duration for holiday %u.", event.holiday_id); + return; + } + + uint8 stageIndex = event.holidayStage - 1; + event.length = holiday->Duration[stageIndex] * HOUR / MINUTE; + + time_t stageOffset = 0; + for (int i = 0; i < stageIndex; ++i) + stageOffset += holiday->Duration[i] * HOUR; + + switch (holiday->CalendarFilterType) + { + case -1: // Yearly + event.occurence = YEAR / MINUTE; // Not all too useful + break; + case 0: // Weekly + event.occurence = WEEK / MINUTE; + break; + case 1: // Defined dates only (Darkmoon Faire) + break; + case 2: // Only used for looping events (Call to Arms) + break; + } + + if (holiday->Looping) + { + event.occurence = 0; + for (int i = 0; i < MAX_HOLIDAY_DURATIONS && holiday->Duration[i]; ++i) + event.occurence += holiday->Duration[i] * HOUR / MINUTE; + } + + bool singleDate = ((holiday->Date[0] >> 24) & 0x1F) == 31; // Events with fixed date within year have - 1 + + time_t curTime = time(NULL); + for (int i = 0; i < MAX_HOLIDAY_DATES && holiday->Date[i]; ++i) + { + uint32 date = holiday->Date[i]; + + tm timeInfo; + if (singleDate) + timeInfo.tm_year = localtime(&curTime)->tm_year - 1; // First try last year (event active through New Year) + else + timeInfo.tm_year = ((date >> 24) & 0x1F) + 100; + timeInfo.tm_mon = (date >> 20) & 0xF; + timeInfo.tm_mday = ((date >> 14) & 0x3F) + 1; + timeInfo.tm_hour = (date >> 6) & 0x1F; + timeInfo.tm_min = date & 0x3F; + timeInfo.tm_sec = 0; + timeInfo.tm_isdst = -1; + tm tmCopy = timeInfo; + + time_t startTime = mktime(&timeInfo); + if (curTime < startTime + event.length * MINUTE) + { + event.start = startTime + stageOffset; + return; + } + else if (singleDate) + { + tmCopy.tm_year = localtime(&curTime)->tm_year; // This year + event.start = mktime(&tmCopy) + stageOffset; + return; + } + } + sLog->outString("No suitable start date found for holiday %u.", event.holiday_id); +} + bool IsHolidayActive(HolidayIds id) { if (id == HOLIDAY_NONE) diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h index 91eb981102..64f1659882 100644 --- a/src/server/game/Events/GameEventMgr.h +++ b/src/server/game/Events/GameEventMgr.h @@ -50,6 +50,7 @@ struct GameEventData uint32 occurence; // time between end and start uint32 length; // length of the event (minutes) after finishing all conditions HolidayIds holiday_id; + uint8 holidayStage; GameEventState state; // state of the game event, these are saved into the game_event table on change! GameEventConditionMap conditions; // conditions to finish std::set<uint16 /*gameevent id*/> prerequisite_events; // events that must be completed before starting this event @@ -95,6 +96,7 @@ class GameEventMgr bool CheckOneGameEvent(uint16 entry) const; uint32 NextCheck(uint16 entry) const; void LoadFromDB(); + void LoadHolidayDates(); uint32 Update(); bool IsActiveEvent(uint16 event_id) { return (m_ActiveEvents.find(event_id) != m_ActiveEvents.end()); } uint32 StartSystem(); @@ -128,6 +130,7 @@ class GameEventMgr bool hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id); bool hasCreatureActiveEventExcept(uint32 creature_guid, uint16 event_id); bool hasGameObjectActiveEventExcept(uint32 go_guid, uint16 event_id); + void SetHolidayEventTime(GameEventData& event); typedef std::list<uint32> GuidList; typedef std::list<uint32> IdList; @@ -162,6 +165,7 @@ class GameEventMgr public: GameEventGuidMap mGameEventCreatureGuids; GameEventGuidMap mGameEventGameobjectGuids; + std::set<uint32> modifiedHolidays; }; #define sGameEventMgr ACE_Singleton<GameEventMgr, ACE_Null_Mutex>::instance() diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 125b6d6c7c..c82ad20e9c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1171,9 +1171,9 @@ void ObjectMgr::LoadEquipmentTemplates() if (!equipmentInfo.ItemEntry[i]) continue; - ItemEntry const* dbcItem = sItemStore.LookupEntry(equipmentInfo.ItemEntry[i]); + const ItemTemplate* item = GetItemTemplate(equipmentInfo.ItemEntry[i]); - if (!dbcItem) + if (!item) { sLog->outErrorDb("Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u, forced to 0.", equipmentInfo.ItemEntry[i], i+1, entry); @@ -1181,15 +1181,15 @@ void ObjectMgr::LoadEquipmentTemplates() continue; } - if (dbcItem->InventoryType != INVTYPE_WEAPON && - dbcItem->InventoryType != INVTYPE_SHIELD && - dbcItem->InventoryType != INVTYPE_RANGED && - dbcItem->InventoryType != INVTYPE_2HWEAPON && - dbcItem->InventoryType != INVTYPE_WEAPONMAINHAND && - dbcItem->InventoryType != INVTYPE_WEAPONOFFHAND && - dbcItem->InventoryType != INVTYPE_HOLDABLE && - dbcItem->InventoryType != INVTYPE_THROWN && - dbcItem->InventoryType != INVTYPE_RANGEDRIGHT) + if (item->InventoryType != INVTYPE_WEAPON && + item->InventoryType != INVTYPE_SHIELD && + item->InventoryType != INVTYPE_RANGED && + item->InventoryType != INVTYPE_2HWEAPON && + item->InventoryType != INVTYPE_WEAPONMAINHAND && + item->InventoryType != INVTYPE_WEAPONOFFHAND && + item->InventoryType != INVTYPE_HOLDABLE && + item->InventoryType != INVTYPE_THROWN && + item->InventoryType != INVTYPE_RANGEDRIGHT) { sLog->outErrorDb("Item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u is not equipable in a hand, forced to 0.", equipmentInfo.ItemEntry[i], i+1, entry); @@ -2266,7 +2266,6 @@ void ObjectMgr::LoadItemTemplates() _itemTemplateStore.rehash(result->GetRowCount()); uint32 count = 0; - bool enforceDBCAttributes = sWorld->getBoolConfig(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES); do { @@ -2383,53 +2382,6 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.FlagsCu = fields[137].GetUInt32(); // Checks - - ItemEntry const* dbcitem = sItemStore.LookupEntry(entry); - - if (dbcitem) - { - if (itemTemplate.Class != dbcitem->Class) - { - sLog->outErrorDb("Item (Entry: %u) does not have a correct class %u, must be %u .", entry, itemTemplate.Class, dbcitem->Class); - if (enforceDBCAttributes) - itemTemplate.Class = dbcitem->Class; - } - - if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclass) - { - sLog->outError("Item (Entry: %u) does not have a correct SoundOverrideSubclass (%i), must be %i .", entry, itemTemplate.SoundOverrideSubclass, dbcitem->SoundOverrideSubclass); - if (enforceDBCAttributes) - itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclass; - } - if (itemTemplate.Material != dbcitem->Material) - { - sLog->outErrorDb("Item (Entry: %u) does not have a correct material (%i), must be %i .", entry, itemTemplate.Material, dbcitem->Material); - if (enforceDBCAttributes) - itemTemplate.Material = dbcitem->Material; - } - if (itemTemplate.InventoryType != dbcitem->InventoryType) - { - sLog->outErrorDb("Item (Entry: %u) does not have a correct inventory type (%u), must be %u .", entry, itemTemplate.InventoryType, dbcitem->InventoryType); - if (enforceDBCAttributes) - itemTemplate.InventoryType = dbcitem->InventoryType; - } - if (itemTemplate.DisplayInfoID != dbcitem->DisplayId) - { - sLog->outErrorDb("Item (Entry: %u) does not have a correct display id (%u), must be %u .", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayId); - if (enforceDBCAttributes) - itemTemplate.DisplayInfoID = dbcitem->DisplayId; - } - if (itemTemplate.Sheath != dbcitem->Sheath) - { - sLog->outErrorDb("Item (Entry: %u) does not have a correct sheathid (%u), must be %u .", entry, itemTemplate.Sheath, dbcitem->Sheath); - if (enforceDBCAttributes) - itemTemplate.Sheath = dbcitem->Sheath; - } - - } - else - sLog->outErrorDb("Item (Entry: %u) does not exist in item.dbc! (not correct id?).", entry); - if (itemTemplate.Class >= MAX_ITEM_CLASS) { sLog->outErrorDb("Item (Entry: %u) has wrong Class value (%u)", entry, itemTemplate.Class); @@ -2720,7 +2672,7 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.ItemSet = 0; } - if (itemTemplate.Area && !GetAreaEntryByAreaID(itemTemplate.Area)) + if (itemTemplate.Area && !sAreaTableStore.LookupEntry(itemTemplate.Area)) sLog->outErrorDb("Item (Entry: %u) has wrong Area (%u)", entry, itemTemplate.Area); if (itemTemplate.Map && !sMapStore.LookupEntry(itemTemplate.Map)) @@ -4022,7 +3974,7 @@ void ObjectMgr::LoadQuests() // client quest log visual (area case) if (qinfo->ZoneOrSort > 0) { - if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort)) + if (!sAreaTableStore.LookupEntry(qinfo->ZoneOrSort)) { sLog->outErrorDb("Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.", qinfo->GetQuestId(), qinfo->ZoneOrSort); @@ -5845,7 +5797,7 @@ void ObjectMgr::LoadGraveyardZones() continue; } - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry) { sLog->outErrorDb("Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId); @@ -7893,7 +7845,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel() uint32 entry = fields[0].GetUInt32(); int32 skill = fields[1].GetInt16(); - AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry); + AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry); if (!fArea) { sLog->outErrorDb("AreaId %u defined in `skill_fishing_base_level` does not exist", entry); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index d85705b00c..1340400aa6 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -721,6 +721,7 @@ class ObjectMgr CreatureAddon const* GetCreatureTemplateAddon(uint32 entry); ItemTemplate const* GetItemTemplate(uint32 entry); ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; } + std::vector<ItemTemplate*> const* GetItemTemplateStoreFast() const { return &_itemTemplateStoreFast; } ItemSetNameEntry const* GetItemSetNameEntry(uint32 itemId) { diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index ab35c901fd..5554a731e4 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -23,7 +23,7 @@ class Player; #define MAX_NUMBER_OF_GRIDS 64 -#define SIZE_OF_GRIDS 533.33333f +#define SIZE_OF_GRIDS 533.3333f #define CENTER_GRID_ID (MAX_NUMBER_OF_GRIDS/2) #define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2) @@ -213,7 +213,7 @@ namespace Trinity inline bool IsValidMapCoord(float x, float y, float z) { - return IsValidMapCoord(x, y) && isfinite(z); + return IsValidMapCoord(x, y) && IsValidMapCoord(z); } inline bool IsValidMapCoord(float x, float y, float z, float o) diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index 888bf00d77..c09ce07a12 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -34,6 +34,7 @@ Copied events should probably have a new owner #include "GuildMgr.h" #include "ArenaTeamMgr.h" #include "WorldSession.h" +#include "GameEventMgr.h" void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/) { @@ -139,11 +140,10 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/) data.append(dataBuffer); // TODO: Fix this, how we do know how many and what holidays to send? - uint32 holidayCount = 0; - data << uint32(holidayCount); - for (uint32 i = 0; i < holidayCount; ++i) + data << uint32(sGameEventMgr->modifiedHolidays.size()); + for (uint32 entry : sGameEventMgr->modifiedHolidays) { - HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(666); + HolidaysEntry const* holiday = sHolidaysStore.LookupEntry(entry); data << uint32(holiday->Id); // m_ID data << uint32(holiday->Region); // m_region, might be looping diff --git a/src/server/game/Handlers/ChannelHandler.cpp b/src/server/game/Handlers/ChannelHandler.cpp index 9b0cddccfc..a74a3e91c6 100644 --- a/src/server/game/Handlers/ChannelHandler.cpp +++ b/src/server/game/Handlers/ChannelHandler.cpp @@ -27,7 +27,7 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) if (!channel) return; - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetPlayer()->GetZoneId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId()); if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone)) return; } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 5abcd8a8e9..3679c824ca 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1979,30 +1979,50 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket &recvData) item = _player->GetItemByGuid(itemGuid); uint16 dstpos = i | (INVENTORY_SLOT_BAG_0 << 8); + + InventoryResult msg; + + Item* uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (uItem) { + if (uItem->IsEquipped()) { + msg = _player->CanUnequipItem(dstpos, true); + if (msg != EQUIP_ERR_OK) { + _player->SendEquipError(msg, uItem, NULL); + continue; + } + } - if (!item) - { - Item* uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (!uItem) - continue; - - ItemPosCountVec sDest; - InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sDest, uItem, false); - if (msg == EQUIP_ERR_OK) + if (!item) { - _player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true); - _player->StoreItem(sDest, uItem, true); - } - else - _player->SendEquipError(msg, uItem, NULL); + ItemPosCountVec sDest; + msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sDest, uItem, false); + if (msg == EQUIP_ERR_OK) + { + _player->RemoveItem(INVENTORY_SLOT_BAG_0, i, true); + _player->StoreItem(sDest, uItem, true); + } + else + _player->SendEquipError(msg, uItem, NULL); - continue; + continue; + } } - if (item->GetPos() == dstpos) - continue; + if (item) { + if (item->GetPos() == dstpos) + continue; - _player->SwapItem(item->GetPos(), dstpos); + if (!item->IsEquipped()) { + uint16 _candidatePos; + msg = _player->CanEquipItem(NULL_SLOT, _candidatePos, item, true); + if (msg != EQUIP_ERR_OK) { + _player->SendEquipError(msg, item, NULL); + continue; + } + } + + _player->SwapItem(item->GetPos(), dstpos); + } } WorldPacket data(SMSG_EQUIPMENT_SET_USE_RESULT, 1); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index bf15832c50..ec48d7221b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -360,7 +360,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) continue; std::string aname; - if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId())) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(itr->second->GetZoneId())) aname = areaEntry->area_name[GetSessionDbcLocale()]; bool s_show = true; @@ -1934,7 +1934,7 @@ void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recv_data*/) return; } - AreaTableEntry const* atEntry = GetAreaEntryByAreaID(_player->GetAreaId()); + AreaTableEntry const* atEntry = sAreaTableStore.LookupEntry(_player->GetAreaId()); if (!atEntry || !(atEntry->flags & AREA_FLAG_WINTERGRASP_2)) return; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 1feb9ac65e..8d435b303d 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -474,16 +474,26 @@ void WorldSession::HandleMovementOpcodes(WorldPacket & recvData) plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); - if (movementInfo.pos.GetPositionZ() < -500.0f) + if (movementInfo.pos.GetPositionZ() < plrMover->GetMap()->GetMinHeight(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY())) if (!plrMover->GetBattleground() || !plrMover->GetBattleground()->HandlePlayerUnderMap(_player)) { if (plrMover->IsAlive()) { + plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM if (plrMover->IsAlive()) plrMover->KillPlayer(); } + else if (!plrMover->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS)) + { + WorldSafeLocsEntry const* grave = sObjectMgr->GetClosestGraveyard(plrMover->GetPositionX(), plrMover->GetPositionY(), plrMover->GetPositionZ(), plrMover->GetMapId(), plrMover->GetTeamId()); + + if ( grave) + plrMover->TeleportTo(grave->map_id, grave->x, grave->y, grave->z, plrMover->GetOrientation()); + plrMover->Relocate(grave->x, grave->y, grave->z, plrMover->GetOrientation()); + } + plrMover->StopMovingOnCurrentPos(); // pussywizard: moving corpse can't release spirit } } @@ -711,5 +721,5 @@ void WorldSession::HandleSummonResponseOpcode(WorldPacket& recvData) agree = false; } _player->SetSummonAsSpectator(false); - _player->SummonIfPossible(agree); + _player->SummonIfPossible(agree, summoner_guid); } diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index 70c1b5c176..3d1958d604 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -134,6 +134,9 @@ class InstanceScript : public ZoneScript //On load virtual void Load(char const* data) { LoadBossState(data); } + //Called when creature is Looted + virtual void CreatureLooted(Creature* /*creature*/, LootType) {} + //When save is needed, this function generates the data virtual std::string GetSaveData() { return GetBossSaveData(); } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index f1f2875dcf..e25cd1c362 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -84,7 +84,7 @@ class LootTemplate::LootGroup // A set of loot def bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry bool HasQuestDropForPlayer(Player const* player) const; // The same for active quests of the player - void Process(Loot& loot, uint16 lootMode) const; // Rolls an item from the group (if any) and adds the item to the loot + void Process(Loot& loot, Player const *player, LootStore const& lootstore, uint16 lootMode) const; // Rolls an item from the group (if any) and adds the item to the loot float RawTotalChance() const; // Overall chance for the group (without equal chanced items) float TotalChance() const; // Overall chance for the group @@ -98,7 +98,7 @@ class LootTemplate::LootGroup // A set of loot def LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance - LootStoreItem const* Roll(Loot& loot, uint16 lootMode) const; // Rolls an item from the group, returns NULL if all miss their chances + LootStoreItem const* Roll(Loot& loot, Player const *player, LootStore const& store, uint16 lootMode) const; // Rolls an item from the group, returns NULL if all miss their chances // This class must never be copied - storing pointers LootGroup(LootGroup const&); @@ -279,19 +279,23 @@ void LootStore::ReportNotExistedId(uint32 id) const // Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation) // RATE_DROP_ITEMS is no longer used for all types of entries -bool LootStoreItem::Roll(bool rate) const +bool LootStoreItem::Roll(bool rate, Player const *player, Loot& loot, LootStore const& store) const { - if (chance >= 100.0f) + float _chance = chance; + + sScriptMgr->OnItemRoll(player, this, _chance, loot, store); + + if (_chance >= 100.0f) return true; if (mincountOrRef < 0) // reference case - return roll_chance_f(chance* (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); + return roll_chance_f(_chance* (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); float qualityModifier = pProto && rate ? sWorld->getRate(qualityToRate[pProto->Quality]) : 1.0f; - return roll_chance_f(chance*qualityModifier); + return roll_chance_f(_chance*qualityModifier); } // Checks correctness of values @@ -462,7 +466,7 @@ bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bo items.reserve(MAX_NR_LOOT_ITEMS); quest_items.reserve(MAX_NR_QUEST_ITEMS); - tab->Process(*this, store.IsRatesAllowed(), lootMode, lootOwner); // Processing is done there, callback via Loot::AddItem() + tab->Process(*this, store, lootMode, lootOwner); // Processing is done there, callback via Loot::AddItem() // Setting access rights for group loot case Group* group = lootOwner->GetGroup(); @@ -1102,7 +1106,7 @@ void LootTemplate::LootGroup::AddEntry(LootStoreItem* item) } // Rolls an item from the group, returns NULL if all miss their chances -LootStoreItem const* LootTemplate::LootGroup::Roll(Loot& loot, uint16 lootMode) const +LootStoreItem const* LootTemplate::LootGroup::Roll(Loot& loot, Player const *player, LootStore const& store, uint16 lootMode) const { LootStoreItemList possibleLoot = ExplicitlyChanced; possibleLoot.remove_if(LootGroupInvalidSelector(loot, lootMode)); @@ -1114,10 +1118,14 @@ LootStoreItem const* LootTemplate::LootGroup::Roll(Loot& loot, uint16 lootMode) for (LootStoreItemList::const_iterator itr = possibleLoot.begin(); itr != possibleLoot.end(); ++itr) // check each explicitly chanced entry in the template and modify its chance based on quality. { LootStoreItem* item = *itr; - if (item->chance >= 100.0f) + float chance = item->chance; + + sScriptMgr->OnItemRoll(player, item, chance, loot, store); + + if (chance >= 100.0f) return item; - roll -= item->chance; + roll -= chance; if (roll < 0) return item; } @@ -1169,9 +1177,9 @@ void LootTemplate::LootGroup::CopyConditions(ConditionList /*conditions*/) } // Rolls an item from the group (if any takes its chance) and adds the item to the loot -void LootTemplate::LootGroup::Process(Loot& loot, uint16 lootMode) const +void LootTemplate::LootGroup::Process(Loot& loot, Player const *player, LootStore const& store, uint16 lootMode) const { - if (LootStoreItem const* item = Roll(loot, lootMode)) + if (LootStoreItem const* item = Roll(loot, player, store, lootMode)) loot.AddItem(*item); } @@ -1297,8 +1305,10 @@ void LootTemplate::CopyConditions(LootItem* li) const } // Rolls for every item in the template and adds the rolled items the the loot -void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, Player const* player, uint8 groupId) const +void LootTemplate::Process(Loot& loot, LootStore const& store, uint16 lootMode, Player const* player, uint8 groupId) const { + bool rate = store.IsRatesAllowed(); + if (groupId) // Group reference uses own processing of the group { if (groupId > Groups.size()) @@ -1307,7 +1317,7 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, Player const* if (!Groups[groupId - 1]) return; - Groups[groupId-1]->Process(loot, lootMode); + Groups[groupId-1]->Process(loot, player, store, lootMode); return; } @@ -1318,7 +1328,7 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, Player const* if (!(item->lootmode & lootMode)) // Do not add if mode mismatch continue; - if (!item->Roll(rate)) + if (!item->Roll(rate, player, loot, store)) continue; // Bad luck for the entry if (item->mincountOrRef < 0) // References processing @@ -1328,12 +1338,12 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, Player const* continue; // Error message already printed at loading stage uint32 maxcount = uint32(float(item->maxcount) * sWorld->getRate(RATE_DROP_ITEM_REFERENCED_AMOUNT)); - sScriptMgr->OnAfterRefCount(item, maxcount); + sScriptMgr->OnAfterRefCount(player, loot, rate, lootMode, item, maxcount, store); for (uint32 loop = 0; loop < maxcount; ++loop) // Ref multiplicator - Referenced->Process(loot, rate, lootMode, player, item->group); + Referenced->Process(loot, store, lootMode, player, item->group); } else { // Plain entries (not a reference, not grouped) - sScriptMgr->OnBeforeDropAddItem(player, loot, item); + sScriptMgr->OnBeforeDropAddItem(player, loot, rate, lootMode, item, store); loot.AddItem(*item); // Chance is already checked, just add } } @@ -1341,7 +1351,7 @@ void LootTemplate::Process(Loot& loot, bool rate, uint16 lootMode, Player const* // Now processing groups for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i) if (LootGroup* group = *i) - group->Process(loot, lootMode); + group->Process(loot, player, store, lootMode); } // True if template includes at least 1 quest drop entry @@ -1598,8 +1608,8 @@ void LoadLootTemplates_Fishing() uint32 count = LootTemplates_Fishing.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot - for (uint32 i = 1; i < sAreaStore.GetNumRows(); ++i) - if (AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i)) + for (uint32 i = 1; i < sAreaTableStore.GetNumRows(); ++i) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i)) if (lootIdSet.find(areaEntry->ID) != lootIdSet.end()) lootIdSet.erase(areaEntry->ID); @@ -1784,8 +1794,8 @@ void LoadLootTemplates_Mail() uint32 count = LootTemplates_Mail.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot - for (uint32 i = 1; i < sMailTemplateStore.GetNumRows(); ++i) - if (sMailTemplateStore.LookupEntry(i)) + for (uint32 i = 1; i < sAreaTableStore.GetNumRows(); ++i) + if (sAreaTableStore.LookupEntry(i)) if (lootIdSet.find(i) != lootIdSet.end()) lootIdSet.erase(i); diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 4af00d3f8b..fc08f030af 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -110,6 +110,7 @@ enum LootSlotType class Player; class LootStore; class ConditionMgr; +struct Loot; struct LootStoreItem { @@ -129,7 +130,7 @@ struct LootStoreItem group(_group), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount) {} - bool Roll(bool rate) const; // Checks if the entry takes it's chance (at loot generation) + bool Roll(bool rate, Player const *player, Loot& loot, LootStore const& store) const; // Checks if the entry takes it's chance (at loot generation) bool IsValid(LootStore const& store, uint32 entry) const; // Checks correctness of values }; @@ -178,7 +179,6 @@ struct QuestItem : index(_index), is_looted(_islooted) {} }; -struct Loot; class LootTemplate; typedef std::vector<QuestItem> QuestItemList; @@ -237,7 +237,7 @@ class LootTemplate // Adds an entry to the group (at loading stage) void AddEntry(LootStoreItem* item); // Rolls for every item in the template and adds the rolled items the the loot - void Process(Loot& loot, bool rate, uint16 lootMode, Player const* player, uint8 groupId = 0) const; + void Process(Loot& loot, LootStore const& store, uint16 lootMode, Player const* player, uint8 groupId = 0) const; void CopyConditions(ConditionList conditions); void CopyConditions(LootItem* li) const; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index a14b89306d..4e6a595a55 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -32,7 +32,7 @@ union u_map_magic }; u_map_magic MapMagic = { {'M','A','P','S'} }; -u_map_magic MapVersionMagic = { {'v','1','.','3'} }; +u_map_magic MapVersionMagic = { {'v','1','.','8'} }; u_map_magic MapAreaMagic = { {'A','R','E','A'} }; u_map_magic MapHeightMagic = { {'M','H','G','T'} }; u_map_magic MapLiquidMagic = { {'M','L','I','Q'} }; @@ -1200,13 +1200,15 @@ GridMap::GridMap() _flags = 0; // Area data _gridArea = 0; - _areaMap = NULL; + _areaMap = nullptr; // Height level data _gridHeight = INVALID_HEIGHT; _gridGetHeight = &GridMap::getHeightFromFlat; _gridIntHeightMultiplier = 0; - m_V9 = NULL; - m_V8 = NULL; + m_V9 = nullptr; + m_V8 = nullptr; + _maxHeight = nullptr; + _minHeight = nullptr; // Liquid data _liquidType = 0; _liquidOffX = 0; @@ -1214,9 +1216,9 @@ GridMap::GridMap() _liquidWidth = 0; _liquidHeight = 0; _liquidLevel = INVALID_HEIGHT; - _liquidEntry = NULL; - _liquidFlags = NULL; - _liquidMap = NULL; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; } GridMap::~GridMap() @@ -1277,15 +1279,19 @@ void GridMap::unloadData() delete[] _areaMap; delete[] m_V9; delete[] m_V8; + delete[] _maxHeight; + delete[] _minHeight; delete[] _liquidEntry; delete[] _liquidFlags; delete[] _liquidMap; - _areaMap = NULL; - m_V9 = NULL; - m_V8 = NULL; - _liquidEntry = NULL; - _liquidFlags = NULL; - _liquidMap = NULL; + _areaMap = nullptr; + m_V9 = nullptr; + m_V8 = nullptr; + _maxHeight = nullptr; + _minHeight = nullptr; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; _gridGetHeight = &GridMap::getHeightFromFlat; } @@ -1350,6 +1356,16 @@ bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/) } else _gridGetHeight = &GridMap::getHeightFromFlat; + + if (header.flags & MAP_HEIGHT_HAS_FLIGHT_BOUNDS) + { + _maxHeight = new int16[3 * 3]; + _minHeight = new int16[3 * 3]; + if (fread(_maxHeight, sizeof(int16), 3 * 3, in) != 3 * 3 || + fread(_minHeight, sizeof(int16), 3 * 3, in) != 3 * 3) + return false; + } + return true; } @@ -1620,6 +1636,66 @@ float GridMap::getHeightFromUint16(float x, float y) const return (float)((a * x) + (b * y) + c)*_gridIntHeightMultiplier + _gridHeight; } +float GridMap::getMinHeight(float x, float y) const +{ + if (!_minHeight) + return -500.0f; + + static uint32 const indices[] = + { + 3, 0, 4, + 0, 1, 4, + 1, 2, 4, + 2, 5, 4, + 5, 8, 4, + 8, 7, 4, + 7, 6, 4, + 6, 3, 4 + }; + + static float const boundGridCoords[] = + { + 0.0f, 0.0f, + 0.0f, -266.66666f, + 0.0f, -533.33331f, + -266.66666f, 0.0f, + -266.66666f, -266.66666f, + -266.66666f, -533.33331f, + -533.33331f, 0.0f, + -533.33331f, -266.66666f, + -533.33331f, -533.33331f + }; + + Cell cell(x, y); + float gx = x - (int32(cell.GridX()) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; + float gy = y - (int32(cell.GridY()) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; + + uint32 quarterIndex = 0; + if (cell.CellY() < MAX_NUMBER_OF_CELLS / 2) + { + if (cell.CellX() < MAX_NUMBER_OF_CELLS / 2) + { + quarterIndex = 4 + (gy > gx); + } + else + quarterIndex = 2 + ((-SIZE_OF_GRIDS - gx) > gy); + } + else if (cell.CellX() < MAX_NUMBER_OF_CELLS / 2) + { + quarterIndex = 6 + ((-SIZE_OF_GRIDS - gx) <= gy); + } + else + quarterIndex = gx > gy; + + quarterIndex *= 3; + + return G3D::Plane( + G3D::Vector3(boundGridCoords[indices[quarterIndex + 0] * 2 + 0], boundGridCoords[indices[quarterIndex + 0] * 2 + 1], _minHeight[indices[quarterIndex + 0]]), + G3D::Vector3(boundGridCoords[indices[quarterIndex + 1] * 2 + 0], boundGridCoords[indices[quarterIndex + 1] * 2 + 1], _minHeight[indices[quarterIndex + 1]]), + G3D::Vector3(boundGridCoords[indices[quarterIndex + 2] * 2 + 0], boundGridCoords[indices[quarterIndex + 2] * 2 + 1], _minHeight[indices[quarterIndex + 2]]) + ).distance(G3D::Vector3(gx, gy, 0.0f)); +} + float GridMap::getLiquidLevel(float x, float y) const { if (!_liquidMap) @@ -1679,12 +1755,12 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R uint32 liqTypeIdx = liquidEntry->Type; if (entry < 21) { - if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(getArea(x, y), MAPID_INVALID)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; if (!overrideLiquid && area->zone) { - area = GetAreaEntryByAreaID(area->zone); + area = sAreaTableStore.LookupEntry(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; } @@ -1835,7 +1911,7 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float // we are already under the surface or vmap height above map heigt // or if the distance of the vmap height is less the land height distance - if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z)) + if (vmapHeight > mapHeight || fabs(mapHeight-z) > fabs(vmapHeight-z)) return vmapHeight; else return mapHeight; // better use .map surface height @@ -1847,6 +1923,15 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float return mapHeight; // explicitly use map data } +float Map::GetMinHeight(float x, float y) const +{ + if (GridMap const* grid = const_cast<Map*>(this)->GetGrid(x, y)) + return grid->getMinHeight(x, y); + + return -500.0f; +} + + inline bool IsOutdoorWMO(uint32 mogpFlags, int32 /*adtId*/, int32 /*rootId*/, int32 /*groupId*/, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry) { bool outdoor = true; @@ -1887,7 +1972,7 @@ bool Map::IsOutdoors(float x, float y, float z) const #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug("Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId); #endif - atEntry = GetAreaEntryByAreaID(wmoEntry->areaId); + atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); } return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); } @@ -1911,7 +1996,7 @@ bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, in return false; } -uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const +uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const { uint32 mogpFlags; int32 adtId, rootId, groupId; @@ -1924,20 +2009,20 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const haveAreaInfo = true; wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); if (wmoEntry) - atEntry = GetAreaEntryByAreaID(wmoEntry->areaId); + atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); } - uint16 areaflag; + uint16 areaId = 0; if (atEntry) - areaflag = atEntry->exploreFlag; + areaId = atEntry->ID; else { if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y)) - areaflag = gmap->getArea(x, y); + areaId = gmap->getArea(x, y); // this used while not all *.map files generated (instances) - else - areaflag = GetAreaFlagByMapId(i_mapEntry->MapID); + if (!areaId) + areaId = i_mapEntry->linked_zone; } if (isOutdoors) @@ -1947,8 +2032,31 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const else *isOutdoors = true; } - return areaflag; - } + return areaId; +} + +uint32 Map::GetAreaId(float x, float y, float z) const +{ + return GetAreaId(x, y, z, nullptr); +} + +uint32 Map::GetZoneId(float x, float y, float z) const +{ + uint32 areaId = GetAreaId(x, y, z); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) + if (area->zone) + return area->zone; + + return areaId; +} + +void Map::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const +{ + areaid = zoneid = GetAreaId(x, y, z); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid)) + if (area->zone) + zoneid = area->zone; +} uint8 Map::GetTerrainType(float x, float y) const { @@ -1986,12 +2094,12 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp if (liquid_type && liquid_type < 21) { - if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(GetAreaFlag(x, y, z), GetId())) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; if (!overrideLiquid && area->zone) { - area = GetAreaEntryByAreaID(area->zone); + area = sAreaTableStore.LookupEntry(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; } @@ -2053,34 +2161,6 @@ float Map::GetWaterLevel(float x, float y) const return 0; } -uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - if (entry) - return entry->ID; - else - return 0; -} - -uint32 Map::GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - if (entry) - return (entry->zone != 0) ? entry->zone : entry->ID; - else - return 0; -} - -void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - areaid = entry ? entry->ID : 0; - zoneid = entry ? ((entry->zone != 0) ? entry->zone : entry->ID) : 0; -} - bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const { return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 93061f4a3f..aa8939638e 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -85,9 +85,10 @@ struct map_areaHeader uint16 gridArea; }; -#define MAP_HEIGHT_NO_HEIGHT 0x0001 -#define MAP_HEIGHT_AS_INT16 0x0002 -#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_NO_HEIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_HAS_FLIGHT_BOUNDS 0x0008 struct map_heightHeader { @@ -153,6 +154,8 @@ class GridMap uint16* m_uint16_V8; uint8* m_uint8_V8; }; + int16* _maxHeight; + int16* _minHeight; // Height level data float _gridHeight; float _gridIntHeightMultiplier; @@ -193,6 +196,7 @@ public: uint16 getArea(float x, float y) const; inline float getHeight(float x, float y) const {return (this->*_gridGetHeight)(x, y);} + float getMinHeight(float x, float y) const; float getLiquidLevel(float x, float y) const; uint8 getTerrainType(float x, float y) const; ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = 0); @@ -327,12 +331,16 @@ class Map : public GridRefManager<NGridType> // some calls like isInWater should not use vmaps due to processor power // can return INVALID_HEIGHT if under z+2 z coord not found height float GetHeight(float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; + float GetMinHeight(float x, float y) const; Transport* GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject = NULL); ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = 0) const; - uint16 GetAreaFlag(float x, float y, float z, bool *isOutdoors=0) const; - bool GetAreaInfo(float x, float y, float z, uint32 &mogpflags, int32 &adtId, int32 &rootId, int32 &groupId) const; + uint32 GetAreaId(float x, float y, float z, bool *isOutdoors) const; + bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; + uint32 GetAreaId(float x, float y, float z) const; + uint32 GetZoneId(float x, float y, float z) const; + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const; bool IsOutdoors(float x, float y, float z) const; @@ -341,25 +349,6 @@ class Map : public GridRefManager<NGridType> bool IsInWater(float x, float y, float z, LiquidData* data = 0) const; bool IsUnderWater(float x, float y, float z) const; - static uint32 GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id); - static uint32 GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id); - static void GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id); - - uint32 GetAreaId(float x, float y, float z) const - { - return GetAreaIdByAreaFlag(GetAreaFlag(x, y, z), GetId()); - } - - uint32 GetZoneId(float x, float y, float z) const - { - return GetZoneIdByAreaFlag(GetAreaFlag(x, y, z), GetId()); - } - - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const - { - GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(x, y, z), GetId()); - } - void MoveAllCreaturesInMoveList(); void MoveAllGameObjectsInMoveList(); void MoveAllDynamicObjectsInMoveList(); diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index fc7c158da8..d8ca015251 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -36,22 +36,20 @@ class MapManager return (iter == i_maps.end() ? NULL : iter->second); } - uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const - { - Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); - return m->GetAreaFlag(x, y, z); - } uint32 GetAreaId(uint32 mapid, float x, float y, float z) const { - return Map::GetAreaIdByAreaFlag(GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); + return m->GetAreaId(x, y, z); } uint32 GetZoneId(uint32 mapid, float x, float y, float z) const { - return Map::GetZoneIdByAreaFlag(GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); + return m->GetZoneId(x, y, z); } void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) { - Map::GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast<MapManager*>(this)->CreateBaseMap(mapid); + m->GetZoneAndAreaId(zoneid, areaid, x, y, z); } void Initialize(void); diff --git a/src/server/game/Misc/WhoListCache.cpp b/src/server/game/Misc/WhoListCache.cpp index b945a61dfd..7b6873f1c7 100644 --- a/src/server/game/Misc/WhoListCache.cpp +++ b/src/server/game/Misc/WhoListCache.cpp @@ -35,7 +35,7 @@ void WhoListCacheMgr::Update() wstrToLower(wgname); std::string aname; - if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId())) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(itr->second->GetZoneId())) aname = areaEntry->area_name[sWorld->GetDefaultDbcLocale()]; if (itr->second->IsSpectator()) diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index ee6b5b4262..751024f46a 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -444,7 +444,7 @@ enum SpellAttr4 enum SpellAttr5 { - SPELL_ATTR5_UNK0 = 0x00000001, // 0 + SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING = 0x00000001, // 0 SPELL_ATTR5_NO_REAGENT_WHILE_PREP = 0x00000002, // 1 not need reagents if UNIT_FLAG_PREPARATION SPELL_ATTR5_REMOVE_ON_ARENA_ENTER = 0x00000004, // 2 xinef: remove this aura on arena enter SPELL_ATTR5_USABLE_WHILE_STUNNED = 0x00000008, // 3 usable while stunned @@ -3531,7 +3531,7 @@ enum PartyResult }; #define MMAP_MAGIC 0x4d4d4150 // 'MMAP' -#define MMAP_VERSION 3 +#define MMAP_VERSION 8 struct MmapTileHeader { @@ -3539,12 +3539,23 @@ struct MmapTileHeader uint32 dtVersion; uint32 mmapVersion; uint32 size; - bool usesLiquids : 1; + char usesLiquids; + char padding[3]; MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION), - mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {} + mmapVersion(MMAP_VERSION), size(0), usesLiquids(true), padding() { } }; +// All padding fields must be handled and initialized to ensure mmaps_generator will produce binary-identical *.mmtile files +static_assert(sizeof(MmapTileHeader) == 20, "MmapTileHeader size is not correct, adjust the padding field size"); +static_assert(sizeof(MmapTileHeader) == (sizeof(MmapTileHeader::mmapMagic) + + sizeof(MmapTileHeader::dtVersion) + + sizeof(MmapTileHeader::mmapVersion) + + sizeof(MmapTileHeader::size) + + sizeof(MmapTileHeader::usesLiquids) + + sizeof(MmapTileHeader::padding)), "MmapTileHeader has uninitialized padding fields"); + + enum NavTerrain { NAV_EMPTY = 0x00, diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index 0962c63aa5..0e74cdbdb8 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -12,6 +12,7 @@ #include "MoveSplineInit.h" #include "MoveSpline.h" #include "Player.h" +#include "VMapFactory.h" #define MIN_QUIET_DISTANCE 28.0f #define MAX_QUIET_DISTANCE 43.0f @@ -32,6 +33,19 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T* owner) if (!_getPoint(owner, x, y, z)) return; + // Add LOS check for target point + bool isInLOS = VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(owner->GetMapId(), + owner->GetPositionX(), + owner->GetPositionY(), + owner->GetPositionZ() + 2.0f, + x, y, z + 2.0f); + + if (!isInLOS) + { + i_nextCheckTime.Reset(500); + return; + } + owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); Movement::MoveSplineInit init(owner); diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp index c42ae417d2..0a55b66d23 100644 --- a/src/server/game/Movement/MovementGenerators/PathGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PathGenerator.cpp @@ -152,9 +152,10 @@ dtPolyRef PathGenerator::GetPolyByLocation(float* point, float* distance) const // still nothing .. // try with bigger search box - extents[1] = 80.0f; - result = _navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint); - if (DT_SUCCESS == result && polyRef != INVALID_POLYREF) + // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly) + extents[1] = 50.0f; + + if (dtStatusSucceed(_navMeshQuery->findNearestPoly(point, extents, &_filter, &polyRef, closestPoint)) && polyRef != INVALID_POLYREF) { *distance = dtVdist(closestPoint, point); return polyRef; @@ -339,7 +340,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con if (startPoly != endPoly || !endInWaterFar) { float closestPoint[VERTEX_SIZE]; - if (DT_SUCCESS == _navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)) + if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint, NULL))) { dtVcopy(endPoint, closestPoint); SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1])); @@ -416,7 +417,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con // 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)) + if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, NULL))) { // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that // try to recover by using prev polyref @@ -424,7 +425,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con if (prefixPolyLength) { suffixStartPoly = _pathPolyRefs[prefixPolyLength-1]; - if (DT_SUCCESS != _navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)) + if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint,NULL))) error = true; } else @@ -445,7 +446,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con (int*)&suffixPolyLength, MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path - if (!suffixPolyLength || dtResult != DT_SUCCESS) + if (!_polyLength || dtStatusFailed(dtResult)) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update @@ -470,7 +471,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con (int*)&_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path - if (!_polyLength || dtResult != DT_SUCCESS) + if (!_polyLength || dtStatusFailed(dtResult)) { // only happens if we passed bad data to findPath(), or navmesh is messed up BuildShortcut(); @@ -499,7 +500,7 @@ void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 con (int*)&_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path - if (!_polyLength || dtResult != DT_SUCCESS) + if (!_polyLength || dtStatusFailed(dtResult)) { // only happens if we passed bad data to findPath(), or navmesh is messed up BuildShortcut(); @@ -658,7 +659,7 @@ void PathGenerator::BuildPointPath(const float *startPoint, const float *endPoin _pointPathLimit); // maximum number of points } - if (pointCount < 2 || dtResult != DT_SUCCESS) + if (pointCount < 2 || dtStatusFailed(dtResult)) { // only happens if pass bad data to findStraightPath or navmesh is broken // single point paths can be generated here @@ -784,7 +785,7 @@ bool PathGenerator::HaveTile(const G3D::Vector3& p) const if (tx < 0 || ty < 0) return false; - return (_navMesh->getTileAt(tx, ty) != NULL); + return (_navMesh->getTileAt(tx, ty, 0) != NULL); } uint32 PathGenerator::FixupCorridor(dtPolyRef* path, uint32 npath, uint32 maxPath, dtPolyRef const* visited, uint32 nvisited) @@ -844,7 +845,7 @@ bool PathGenerator::GetSteerTarget(float const* startPos, float const* endPos, uint32 nsteerPath = 0; dtStatus dtResult = _navMeshQuery->findStraightPath(startPos, endPos, path, pathSize, steerPath, steerPathFlags, steerPathPolys, (int*)&nsteerPath, MAX_STEER_POINTS); - if (!nsteerPath || DT_SUCCESS != dtResult) + if (!nsteerPath || dtStatusFailed(dtResult)) return false; // Find vertex far enough to steer to. @@ -908,7 +909,7 @@ dtStatus PathGenerator::FindSmoothPath(float const* startPos, float const* endPo // Find movement delta. float delta[VERTEX_SIZE]; dtVsub(delta, steerPos, iterPos); - float len = dtSqrt(dtVdot(delta,delta)); + float len = dtMathSqrtf(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; diff --git a/src/server/game/Movement/MovementGenerators/PathGenerator.h b/src/server/game/Movement/MovementGenerators/PathGenerator.h index 7632c47409..b6c6e72e0d 100644 --- a/src/server/game/Movement/MovementGenerators/PathGenerator.h +++ b/src/server/game/Movement/MovementGenerators/PathGenerator.h @@ -38,7 +38,7 @@ class Unit; #define ALLOWED_DIST_FROM_POLY 2.5f #define ADDED_Z_FOR_POLY_LOOKUP 0.3f #define DISALLOW_TIME_AFTER_FAIL 3 // secs -#define MAX_FIXABLE_Z_ERROR 12.0f +#define MAX_FIXABLE_Z_ERROR 7.0f #define VERTEX_SIZE 3 #define INVALID_POLYREF 0 diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index dd5bf42511..97ffaa4437 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -30,6 +30,9 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool ini if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) return; + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) + return; + float x, y, z; bool isPlayerPet = owner->IsPet() && IS_PLAYER_GUID(owner->GetOwnerGUID()); bool sameTransport = owner->GetTransport() && owner->GetTransport() == i_target->GetTransport(); @@ -44,11 +47,17 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool ini (i_target->GetTypeId() == TYPEID_PLAYER && i_target->ToPlayer()->IsGameMaster()); // for .npc follow bool forcePoint = ((!isPlayerPet || owner->GetMapId() == 618) && (forceDest || !useMMaps)) || sameTransport; + if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature()) && !sameTransport && !forceDest && !forcePoint) + return; + lastOwnerXYZ.Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ()); lastTargetXYZ.Relocate(i_target->GetPositionX(), i_target->GetPositionY(), i_target->GetPositionZ()); if (!i_offset) { + if (i_target->IsWithinDistInMap(owner, CONTACT_DISTANCE)) + return; + float allowedRange = MELEE_RANGE; if ((!initial || (owner->movespline->Finalized() && this->GetMovementGeneratorType() == CHASE_MOTION_TYPE)) && i_target->IsWithinMeleeRange(owner, allowedRange) && i_target->IsWithinLOS(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ())) return; @@ -60,6 +69,9 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool ini owner->m_targetsNotAcceptable[i_target->GetGUID()] = MMapTargetData(sWorld->GetGameTime()+DISALLOW_TIME_AFTER_FAIL, owner, i_target.getTarget()); return; } + + // to nearest contact position + i_target->GetContactPoint(owner, x, y, z); } else { @@ -167,6 +179,7 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool ini else { owner->m_targetsNotAcceptable.erase(i_target->GetGUID()); + owner->AddUnitState(UNIT_STATE_CHASE); init.MovebyPath(i_path->GetPath()); if (i_angle == 0.f) @@ -180,6 +193,8 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T* owner, bool ini // if failed to generate, just use normal MoveTo } + owner->AddUnitState(UNIT_STATE_CHASE); + init.MoveTo(x,y,z); // Using the same condition for facing target as the one that is used for SetInFront on movement end // - applies to ChaseMovementGenerator mostly @@ -206,7 +221,7 @@ bool TargetedMovementGeneratorMedium<T,D>::DoUpdate(T* owner, uint32 time_diff) } // prevent movement while casting spells with cast time or channel time - if (owner->HasUnitState(UNIT_STATE_CASTING)) + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) { bool stop = true; if (Spell* spell = owner->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 876c63e4c5..2c2bf165c6 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1148,13 +1148,13 @@ void ScriptMgr::OnShutdown() FOREACH_SCRIPT(WorldScript)->OnShutdown(); } -bool ScriptMgr::OnCriteriaCheck(uint32 scriptId, Player* source, Unit* target) +bool ScriptMgr::OnCriteriaCheck(uint32 scriptId, Player* source, Unit* target, uint32 criteria_id) { ASSERT(source); // target can be NULL. GET_SCRIPT_RET(AchievementCriteriaScript, scriptId, tmpscript, false); - return tmpscript->OnCheck(source, target); + return tmpscript->OnCheck(source, target, criteria_id); } // Player @@ -1303,6 +1303,21 @@ void ScriptMgr::OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newAre FOREACH_SCRIPT(PlayerScript)->OnUpdateZone(player, newZone, newArea); } +void ScriptMgr::OnPlayerUpdateArea(Player* player, uint32 oldArea, uint32 newArea) +{ + FOREACH_SCRIPT(PlayerScript)->OnUpdateArea(player, oldArea, newArea); +} + +bool ScriptMgr::OnBeforePlayerTeleport(Player* player, uint32 mapid, float x, float y, float z, float orientation, uint32 options, Unit *target) +{ + bool ret=true; + FOR_SCRIPTS_RET(PlayerScript, itr, end, ret) // return true by default if not scripts + if (!itr->second->OnBeforeTeleport(player, mapid, x, y, z, orientation, options, target)) + ret=false; // we change ret value only when scripts return false + + return ret; +} + void ScriptMgr::OnPlayerUpdateFaction(Player* player) { FOREACH_SCRIPT(PlayerScript)->OnUpdateFaction(player); @@ -1494,14 +1509,18 @@ void ScriptMgr::OnBeforeUpdateArenaPoints(ArenaTeam* at, std::map<uint32, uint32 FOREACH_SCRIPT(GlobalScript)->OnBeforeUpdateArenaPoints(at,ap); } -void ScriptMgr::OnAfterRefCount(LootStoreItem* LootStoreItem, uint32 &maxcount) +void ScriptMgr::OnAfterRefCount(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, uint32 &maxcount, LootStore const& store) { - FOREACH_SCRIPT(GlobalScript)->OnAfterRefCount(LootStoreItem, maxcount); + FOREACH_SCRIPT(GlobalScript)->OnAfterRefCount(player, LootStoreItem, loot, canRate, lootMode, maxcount, store); } -void ScriptMgr::OnBeforeDropAddItem(Player const* player, Loot& loot, LootStoreItem* LootStoreItem) +void ScriptMgr::OnBeforeDropAddItem(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, LootStore const& store) { - FOREACH_SCRIPT(GlobalScript)->OnBeforeDropAddItem(player, loot, LootStoreItem); + FOREACH_SCRIPT(GlobalScript)->OnBeforeDropAddItem(player, loot, canRate, lootMode, LootStoreItem, store); +} + +void ScriptMgr::OnItemRoll(Player const* player, LootStoreItem const* LootStoreItem, float &chance, Loot& loot, LootStore const& store) { + FOREACH_SCRIPT(GlobalScript)->OnItemRoll(player, LootStoreItem, chance, loot, store); } void ScriptMgr::OnInitializeLockedDungeons(Player* player, uint8& level, uint32& lockData) diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 260d8d666c..c71c45dbff 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -747,7 +747,11 @@ class AchievementCriteriaScript : public ScriptObject bool IsDatabaseBound() const { return true; } // Called when an additional criteria is checked. - virtual bool OnCheck(Player* source, Unit* target) = 0; + virtual bool OnCheck(Player* source, Unit* target, uint32 /*criteria_id*/) { + return OnCheck(source, target); + } + // deprecated/legacy + virtual bool OnCheck(Player* /*source*/, Unit* /*target*/) { return true; }; }; class PlayerScript : public ScriptObject @@ -838,9 +842,15 @@ class PlayerScript : public ScriptObject // Called when a player switches to a new zone virtual void OnUpdateZone(Player* /*player*/, uint32 /*newZone*/, uint32 /*newArea*/) { } + // Called when a player switches to a new area (more accurate than UpdateZone) + virtual void OnUpdateArea(Player* /*player*/, uint32 /*oldArea*/, uint32 /*newArea*/) { } + // Called when a player changes to a new map (after moving to new map) virtual void OnMapChanged(Player* /*player*/) { } + // Called before a player is being teleported to new coords + virtual bool OnBeforeTeleport(Player* /*player*/, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) { return true; } + // Called when team/faction is set on player virtual void OnUpdateFaction(Player* /*player*/) { } @@ -994,8 +1004,11 @@ class GlobalScript : public ScriptObject // items virtual void OnItemDelFromDB(SQLTransaction& /*trans*/, uint32 /*itemGuid*/) { } virtual void OnMirrorImageDisplayItem(const Item* /*item*/, uint32& /*display*/) { } - virtual void OnAfterRefCount(LootStoreItem* /*LootStoreItem*/, uint32& /*maxcount*/) { } - virtual void OnBeforeDropAddItem(Player const* /*player*/, Loot& /*loot*/, LootStoreItem* /*LootStoreItem*/) { } + + // loot + virtual void OnAfterRefCount(Player const* /*player*/, LootStoreItem* /*LootStoreItem*/, Loot& /*loot*/, bool /*canRate*/, uint16 /*lootMode*/, uint32& /*maxcount*/, LootStore const& /*store*/) { } + virtual void OnBeforeDropAddItem(Player const* /*player*/, Loot& /*loot*/, bool /*canRate*/, uint16 /*lootMode*/, LootStoreItem* /*LootStoreItem*/, LootStore const& /*store*/) { } + virtual void OnItemRoll(Player const* /*player*/, LootStoreItem const* /*LootStoreItem*/, float& /*chance*/, Loot& /*loot*/, LootStore const& /*store*/) { }; virtual void OnInitializeLockedDungeons(Player* /*player*/, uint8& /*level*/, uint32& /*lockData*/) { } virtual void OnAfterInitializeLockedDungeons(Player* /*player*/) { } @@ -1176,7 +1189,7 @@ class ScriptMgr public: /* AchievementCriteriaScript */ - bool OnCriteriaCheck(uint32 scriptId, Player* source, Unit* target); + bool OnCriteriaCheck(uint32 scriptId, Player* source, Unit* target, uint32 criteria_id); public: /* PlayerScript */ @@ -1210,6 +1223,8 @@ class ScriptMgr void OnPlayerDelete(uint64 guid); void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent); void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea); + void OnPlayerUpdateArea(Player* player, uint32 oldArea, uint32 newArea); + bool OnBeforePlayerTeleport(Player* player, uint32 mapid, float x, float y, float z, float orientation, uint32 options, Unit *target); void OnPlayerUpdateFaction(Player* player); void OnPlayerAddToBattleground(Player* player, Battleground* bg); void OnPlayerRemoveFromBattleground(Player* player, Battleground* bg); @@ -1264,8 +1279,9 @@ class ScriptMgr void OnGlobalItemDelFromDB(SQLTransaction& trans, uint32 itemGuid); void OnGlobalMirrorImageDisplayItem(const Item *item, uint32 &display); void OnBeforeUpdateArenaPoints(ArenaTeam* at, std::map<uint32, uint32> &ap); - void OnAfterRefCount(LootStoreItem* LootStoreItem, uint32 &maxcount); - void OnBeforeDropAddItem(Player const* player, Loot& loot, LootStoreItem* LootStoreItem); + void OnAfterRefCount(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, uint32 &maxcount, LootStore const& store); + void OnBeforeDropAddItem(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, LootStore const& store); + void OnItemRoll(Player const* player, LootStoreItem const* LootStoreItem, float &chance, Loot& loot, LootStore const& store); void OnInitializeLockedDungeons(Player* player, uint8& level, uint32& lockData); void OnAfterInitializeLockedDungeons(Player* player); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index b0e436efb5..99759c7329 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1711,20 +1711,16 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app Unit* target = aurApp->GetTarget(); // no-phase is also phase state so same code for apply and remove - uint32 newPhase = 0; - Unit::AuraEffectList const& phases = target->GetAuraEffectsByType(SPELL_AURA_PHASE); - if (!phases.empty()) - for (Unit::AuraEffectList::const_iterator itr = phases.begin(); itr != phases.end(); ++itr) - newPhase |= (*itr)->GetMiscValue(); + uint32 newPhase = target->GetPhaseByAuras(); if (Player* player = target->ToPlayer()) { if (!newPhase) newPhase = PHASEMASK_NORMAL; - // GM-mode have mask 0xFFFFFFFF + // do not change phase to GM with all phases enabled if (player->IsGameMaster()) - newPhase = 0xFFFFFFFF; + newPhase = PHASEMASK_ANYWHERE; player->SetPhaseMask(newPhase, false); player->GetSession()->SendSetPhaseShift(newPhase); @@ -1769,7 +1765,7 @@ void AuraEffect::HandlePhase(AuraApplication const* aurApp, uint8 mode, bool app void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mode, bool apply) const { - if (!(mode & AURA_EFFECT_HANDLE_REAL)) + if (!(mode & AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK)) return; Unit* target = aurApp->GetTarget(); @@ -3582,6 +3578,9 @@ void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 switch (GetId()) { + case 46924: // BladeStorm + target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply); + break; case 34471: // The Beast Within case 19574: // Bestial Wrath mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index d1736893f3..c50ecc2bc0 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -395,7 +395,7 @@ Aura* Aura::Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owne Aura::Aura(SpellInfo const* spellproto, WorldObject* owner, Unit* caster, Item* castItem, uint64 casterGUID) : m_spellInfo(spellproto), m_casterGuid(casterGUID ? casterGUID : caster->GetGUID()), -m_castItemGuid(castItem ? castItem->GetGUID() : 0), m_applyTime(time(NULL)), +m_castItemGuid(castItem ? castItem->GetGUID() : 0),m_castItemEntry(castItem ? castItem->GetEntry() : 0), m_applyTime(time(NULL)), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 30f25dae9b..1587f9fadf 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -96,6 +96,7 @@ class Aura uint32 GetId() const{ return GetSpellInfo()->Id; } uint64 GetCastItemGUID() const { return m_castItemGuid; } + uint32 GetCastItemEntry() const { return m_castItemEntry; } uint64 GetCasterGUID() const { return m_casterGuid; } Unit* GetCaster() const; WorldObject* GetOwner() const { return m_owner; } @@ -235,6 +236,7 @@ class Aura SpellInfo const* const m_spellInfo; uint64 const m_casterGuid; uint64 const m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted + uint32 const m_castItemEntry; // when deleted, we could retrieve some information from template instead time_t const m_applyTime; WorldObject* const m_owner; // diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index f1aa91af71..8bf57153b9 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -287,6 +287,16 @@ Corpse* SpellCastTargets::GetCorpseTarget() const return NULL; } +void SpellCastTargets::SetCorpseTarget(Corpse* target) +{ + if (!target) + return; + + m_objectTarget = target; + m_objectTargetGUID = target->GetGUID(); + m_targetMask |= TARGET_FLAG_CORPSE_MASK; +} + WorldObject* SpellCastTargets::GetObjectTarget() const { return m_objectTarget; @@ -1306,7 +1316,8 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici float dis = (float)rand_norm() * (max_dis - min_dis) + min_dis; float x, y, z, angle; angle = (float)rand_norm() * static_cast<float>(M_PI * 35.0f / 180.0f) - static_cast<float>(M_PI * 17.5f / 180.0f); - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis, angle); + //m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis, angle); this contains extra code that breaks fishing + m_caster->GetNearPoint(m_caster, x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dis, m_caster->GetOrientation() + angle); float ground = m_caster->GetMap()->GetHeight(m_caster->GetPhaseMask(), x, y, z, true, 120.0f); float liquidLevel = VMAP_INVALID_HEIGHT_VALUE; @@ -2395,6 +2406,23 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // Increase time interval for reflected spells by 1.5 m_caster->m_Events.AddEvent(new ReflectEvent(m_caster->GetGUID(), targetInfo.targetGUID, m_spellInfo), m_caster->m_Events.CalculateTime(targetInfo.timeDelay)); targetInfo.timeDelay += targetInfo.timeDelay >> 1; + + // HACK: workaround check for succubus seduction case + // TODO: seduction should be casted only on humanoids (not demons) + if (m_caster->IsPet()) + { + CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(m_caster->GetEntry()); + switch (ci->family) + { + case CREATURE_FAMILY_SUCCUBUS: + { + if (m_spellInfo->SpellIconID != 694) // Soothing Kiss + cancel(); + } + break; + return; + } + } } else targetInfo.reflectResult = SPELL_MISS_NONE; @@ -4545,14 +4573,14 @@ void Spell::WriteAmmoToPacket(WorldPacket* data) { if (uint32 item_id = m_caster->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i)) { - if (ItemEntry const* itemEntry = sItemStore.LookupEntry(item_id)) + if (ItemTemplate const* itemEntry = sObjectMgr->GetItemTemplate(item_id)) { if (itemEntry->Class == ITEM_CLASS_WEAPON) { switch (itemEntry->SubClass) { case ITEM_SUBCLASS_WEAPON_THROWN: - ammoDisplayID = itemEntry->DisplayId; + ammoDisplayID = itemEntry->DisplayInfoID; ammoInventoryType = itemEntry->InventoryType; break; case ITEM_SUBCLASS_WEAPON_BOW: @@ -5788,7 +5816,7 @@ SpellCastResult Spell::CheckCast(bool strict) m_pathFinder = new PathGenerator(m_caster); m_pathFinder->CalculatePath(pos.m_positionX, pos.m_positionY, pos.m_positionZ+0.15f, false); G3D::Vector3 endPos = m_pathFinder->GetEndPosition(); // also check distance between target and the point calculated by mmaps - if (m_pathFinder->GetPathType()&PATHFIND_NOPATH || target->GetExactDistSq(endPos.x, endPos.y, endPos.z) > maxdist*maxdist || m_pathFinder->getPathLength() > (40.0f + (m_caster->HasAura(58097) ? 5.0f : 0.0f))) + if (m_pathFinder->GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE) || target->GetExactDistSq(endPos.x, endPos.y, endPos.z) > maxdist*maxdist || m_pathFinder->getPathLength() > (40.0f + (m_caster->HasAura(58097) ? 5.0f : 0.0f))) return SPELL_FAILED_NOPATH; } break; @@ -6200,7 +6228,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_originalCaster && m_originalCaster->GetTypeId() == TYPEID_PLAYER && m_originalCaster->IsAlive()) { Battlefield* Bf = sBattlefieldMgr->GetBattlefieldToZoneId(m_originalCaster->GetZoneId()); - if (AreaTableEntry const* pArea = GetAreaEntryByAreaID(m_originalCaster->GetAreaId())) + if (AreaTableEntry const* pArea = sAreaTableStore.LookupEntry(m_originalCaster->GetAreaId())) if ((pArea->flags & AREA_FLAG_NO_FLY_ZONE) || (Bf && !Bf->CanFlyIn())) return SPELL_FAILED_NOT_HERE; } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index dd1241ea10..880bb1fdc8 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -107,6 +107,7 @@ class SpellCastTargets uint64 GetCorpseTargetGUID() const; Corpse* GetCorpseTarget() const; + void SetCorpseTarget(Corpse* target); WorldObject* GetObjectTarget() const; uint64 GetObjectTargetGUID() const; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 547a713c4d..4ec091f78a 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3218,7 +3218,7 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) return; // xinef: Hand of Reckoning, cast before checing canhavethreatlist. fixes damage against pets - if (m_spellInfo->Id == 62124 && unitTarget->GetVictim() != m_caster && !unitTarget->IsTotem()) + if (m_spellInfo->Id == 62124 && unitTarget->GetVictim() != m_caster) m_caster->CastSpell(unitTarget, 67485, true); // this effect use before aura Taunt apply for prevent taunt already attacking target @@ -4241,14 +4241,14 @@ void Spell::EffectDuel(SpellEffIndex effIndex) return; // Players can only fight a duel in zones with this flag - AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetAreaId()); + AreaTableEntry const* casterAreaEntry = sAreaTableStore.LookupEntry(caster->GetAreaId()); if (casterAreaEntry && !(casterAreaEntry->flags & AREA_FLAG_ALLOW_DUELS)) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here return; } - AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetAreaId()); + AreaTableEntry const* targetAreaEntry = sAreaTableStore.LookupEntry(target->GetAreaId()); if (targetAreaEntry && !(targetAreaEntry->flags & AREA_FLAG_ALLOW_DUELS)) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 098b5af9c6..d8dbd3aa8f 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1069,7 +1069,7 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 if (!player) return false; - AreaTableEntry const* pArea = GetAreaEntryByAreaID(player->GetAreaId()); + AreaTableEntry const* pArea = sAreaTableStore.LookupEntry(player->GetAreaId()); if (!(pArea && pArea->flags & AREA_FLAG_NO_FLY_ZONE)) return false; if (!player->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !player->HasAuraType(SPELL_AURA_FLY)) @@ -2513,7 +2513,7 @@ void SpellMgr::LoadSpellAreas() } } - if (spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) + if (spellArea.areaId && !sAreaTableStore.LookupEntry(spellArea.areaId)) { sLog->outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell, spellArea.areaId); continue; @@ -6243,8 +6243,8 @@ void SpellMgr::LoadDbcDataCorrections() } // Xinef: The Veiled Sea area in outlands (Draenei zone), client blocks casting flying mounts - for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) - if (AreaTableEntry* areaEntry = const_cast<AreaTableEntry*>(sAreaStore.LookupEntry(i))) + for (uint32 i = 0; i < sAreaTableStore.GetNumRows(); ++i) + if (AreaTableEntry* areaEntry = const_cast<AreaTableEntry*>(sAreaTableStore.LookupEntry(i))) { if (areaEntry->ID == 3479) areaEntry->flags |= AREA_FLAG_NO_FLY_ZONE; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index e7cdd9aa35..3ec460da9e 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -75,6 +75,7 @@ #include "WhoListCache.h" #include "AsyncAuctionListing.h" #include "SavingSystem.h" +#include <VMapManager2.h> ACE_Atomic_Op<ACE_Thread_Mutex, bool> World::m_stopEvent = false; uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; @@ -909,6 +910,12 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_UPTIME_UPDATE] = 1; } + if (reload) + { + m_timers[WUPDATE_UPTIME].SetInterval(m_int_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILLISECONDS); + m_timers[WUPDATE_UPTIME].Reset(); + } + // log db cleanup interval m_int_configs[CONFIG_LOGDB_CLEARINTERVAL] = sConfigMgr->GetIntDefault("LogDB.Opt.ClearInterval", 10); if (int32(m_int_configs[CONFIG_LOGDB_CLEARINTERVAL]) <= 0) @@ -1218,9 +1225,6 @@ void World::LoadConfigSettings(bool reload) // Dungeon finder m_int_configs[CONFIG_LFG_OPTIONSMASK] = sConfigMgr->GetIntDefault("DungeonFinder.OptionsMask", 3); - // DBC_ItemAttributes - m_bool_configs[CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES] = sConfigMgr->GetBoolDefault("DBC.EnforceItemAttributes", true); - // Max instances per hour m_int_configs[CONFIG_MAX_INSTANCES_PER_HOUR] = sConfigMgr->GetIntDefault("AccountInstancesPerHour", 5); @@ -1276,6 +1280,12 @@ void World::SetInitialWorldSettings() sLog->outString("Initializing Scripts..."); sScriptMgr->Initialize(); + ///- Initialize VMapManager function pointers (to untangle game/collision circular deps) + if (VMAP::VMapManager2* vmmgr2 = dynamic_cast<VMAP::VMapManager2*>(VMAP::VMapFactory::createOrGetVMapManager())) + { + vmmgr2->GetLiquidFlagsPtr = &GetLiquidFlags; + } + ///- Initialize config settings LoadConfigSettings(); @@ -1520,7 +1530,8 @@ void World::SetInitialWorldSettings() sPoolMgr->LoadFromDB(); sLog->outString("Loading Game Event Data..."); // must be after loading pools fully - sGameEventMgr->LoadFromDB(); + sGameEventMgr->LoadHolidayDates(); // Must be after loading DBC + sGameEventMgr->LoadFromDB(); // Must be after loading holiday dates sLog->outString("Loading UNIT_NPC_FLAG_SPELLCLICK Data..."); // must be after LoadQuests sObjectMgr->LoadNPCSpellClickSpells(); @@ -1752,11 +1763,16 @@ void World::SetInitialWorldSettings() m_gameTime = time(NULL); m_startTime = m_gameTime; + LoginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, uptime, revision) VALUES(%u, %u, 0, '%s')", + realmID, uint32(m_startTime), _FULLVERSION); // One-time query + m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILLISECONDS); m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILLISECONDS); m_timers[WUPDATE_AUCTIONS].SetCurrent(MINUTE*IN_MILLISECONDS); + m_timers[WUPDATE_UPTIME].SetInterval(m_int_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILLISECONDS); + //Update "uptime" table based on configuration entry in minutes. m_timers[WUPDATE_CORPSES].SetInterval(20 * MINUTE * IN_MILLISECONDS); //erase corpses every 20 minutes @@ -2077,7 +2093,25 @@ void World::Update(uint32 diff) // execute callbacks from sql queries that were queued recently ProcessQueryCallbacks(); - + + /// <li> Update uptime table + if (m_timers[WUPDATE_UPTIME].Passed()) + { + uint32 tmpDiff = uint32(m_gameTime - m_startTime); + uint32 maxOnlinePlayers = GetMaxPlayerCount(); + + m_timers[WUPDATE_UPTIME].Reset(); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_UPTIME_PLAYERS); + + stmt->setUInt32(0, tmpDiff); + stmt->setUInt16(1, uint16(maxOnlinePlayers)); + stmt->setUInt32(2, realmID); + stmt->setUInt32(3, uint32(m_startTime)); + + LoginDatabase.Execute(stmt); + } + ///- Erase corpses once every 20 minutes if (m_timers[WUPDATE_CORPSES].Passed()) { @@ -2765,6 +2799,7 @@ void World::UpdateRealmCharCount(uint32 accountId) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_COUNT); stmt->setUInt32(0, accountId); + stmt->setUInt32(1, accountId); PreparedQueryResultFuture result = CharacterDatabase.AsyncQuery(stmt); m_realmCharCallbacks.insert(result); } diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 96eecf073d..6408e0d2dd 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -60,6 +60,7 @@ enum WorldTimers { WUPDATE_AUCTIONS, WUPDATE_WEATHERS, + WUPDATE_UPTIME, WUPDATE_CORPSES, WUPDATE_EVENTS, WUPDATE_CLEANDB, @@ -147,7 +148,6 @@ enum WorldBoolConfigs CONFIG_AUTOBROADCAST, CONFIG_ALLOW_TICKETS, CONFIG_DELETE_CHARACTER_TICKET_TRACE, - CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, CONFIG_PRESERVE_CUSTOM_CHANNELS, CONFIG_WINTERGRASP_ENABLE, CONFIG_PDUMP_NO_PATHS, diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt index 6804d939fe..a3373a007d 100644 --- a/src/server/scripts/CMakeLists.txt +++ b/src/server/scripts/CMakeLists.txt @@ -78,8 +78,8 @@ message("") include_directories( ${scripts_INCLUDE_DIRS} ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Detour - ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Recast + ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Detour/Include + ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Recast/Include ${CMAKE_SOURCE_DIR}/deps/g3dlite/include ${CMAKE_SOURCE_DIR}/deps/SFMT ${CMAKE_SOURCE_DIR}/deps/zlib diff --git a/src/server/scripts/Commands/CMakeLists.txt b/src/server/scripts/Commands/CMakeLists.txt index 6b9bb2a892..f1e5aa4932 100644 --- a/src/server/scripts/Commands/CMakeLists.txt +++ b/src/server/scripts/Commands/CMakeLists.txt @@ -31,6 +31,7 @@ set(scripts_STAT_SRCS Commands/cs_lookup.cpp Commands/cs_message.cpp Commands/cs_misc.cpp + Commands/cs_mmaps.cpp Commands/cs_modify.cpp Commands/cs_npc.cpp Commands/cs_quest.cpp diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp index 9268272d2d..0967aa170d 100644 --- a/src/server/scripts/Commands/cs_gm.cpp +++ b/src/server/scripts/Commands/cs_gm.cpp @@ -191,14 +191,10 @@ public: return false; } - const uint32 VISUAL_AURA = 37800; std::string param = (char*)args; if (param == "on") { - if (_player->HasAura(VISUAL_AURA, 0)) - _player->RemoveAurasDueToSpell(VISUAL_AURA); - _player->SetGMVisible(true); //_player->UpdateObjectVisibility(); handler->GetSession()->SendNotification(LANG_INVISIBLE_VISIBLE); @@ -208,7 +204,6 @@ public: if (param == "off") { - _player->AddAura(VISUAL_AURA, _player); _player->SetGMVisible(false); //_player->UpdateObjectVisibility(); handler->GetSession()->SendNotification(LANG_INVISIBLE_INVISIBLE); diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 66a7d5f306..4a6235ddee 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -417,7 +417,7 @@ public: uint32 areaId = id ? (uint32)atoi(id) : player->GetZoneId(); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (x < 0 || x > 100 || y < 0 || y > 100 || !areaEntry) { @@ -427,7 +427,7 @@ public: } // update to parent zone if exist (client map show only zones without parents) - AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry; + AreaTableEntry const* zoneEntry = areaEntry->zone ? sAreaTableStore.LookupEntry(areaEntry->zone) : areaEntry; Map const* map = sMapMgr->CreateBaseMap(zoneEntry->mapid); diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index cdfc508d85..9d1c34c5a1 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -548,6 +548,7 @@ public: stmt->setFloat(5, player->GetPositionY()); stmt->setFloat(6, player->GetPositionZ()); stmt->setFloat(7, distance * distance); + stmt->setFloat(8, player->GetPhaseMask()); PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp index d81ce581da..60f5ce165c 100644 --- a/src/server/scripts/Commands/cs_lookup.cpp +++ b/src/server/scripts/Commands/cs_lookup.cpp @@ -86,9 +86,9 @@ public: wstrToLower(wNamePart); // Search in AreaTable.dbc - for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows(); ++areaflag) + for (uint32 i = 0; i < sAreaTableStore.GetNumRows(); ++i) { - AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(areaflag); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i); if (areaEntry) { int locale = handler->GetSessionDbcLocale(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 058290c9e3..268248f5bb 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -400,8 +400,8 @@ public: object->GetZoneAndAreaId(zoneId, areaId); MapEntry const* mapEntry = sMapStore.LookupEntry(object->GetMapId()); - AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zoneId); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); float zoneX = object->GetPositionX(); float zoneY = object->GetPositionY(); @@ -612,8 +612,8 @@ public: else _player->SaveRecallPosition(); - _player->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()+0.25f, _player->GetOrientation(), TELE_TO_GM_MODE); - _player->SetPhaseMask(target->GetPhaseMask() | 1, false); + if (_player->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()+0.25f, _player->GetOrientation(), TELE_TO_GM_MODE, target)) + _player->SetPhaseMask(target->GetPhaseMask() | 1, false); } else { @@ -734,8 +734,8 @@ public: // before GM float x, y, z; handler->GetSession()->GetPlayer()->GetClosePoint(x, y, z, target->GetObjectSize()); - target->TeleportTo(handler->GetSession()->GetPlayer()->GetMapId(), x, y, z, target->GetOrientation()); - target->SetPhaseMask(handler->GetSession()->GetPlayer()->GetPhaseMask(), false); + if (target->TeleportTo(handler->GetSession()->GetPlayer()->GetMapId(), x, y, z, target->GetOrientation(), 0, handler->GetSession()->GetPlayer())) + target->SetPhaseMask(handler->GetSession()->GetPlayer()->GetPhaseMask(), false); } else { @@ -845,7 +845,7 @@ public: // before GM float x, y, z; handler->GetSession()->GetPlayer()->GetClosePoint(x, y, z, player->GetObjectSize()); - player->TeleportTo(handler->GetSession()->GetPlayer()->GetMapId(), x, y, z, player->GetOrientation()); + player->TeleportTo(handler->GetSession()->GetPlayer()->GetMapId(), x, y, z, player->GetOrientation(), 0, handler->GetSession()->GetPlayer()); } return true; @@ -1280,7 +1280,7 @@ public: uint32 zoneId = player->GetZoneId(); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry || areaEntry->zone !=0) { handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, graveyardId, zoneId); @@ -1411,17 +1411,23 @@ public: return false; } - int32 area = GetAreaFlagByAreaID(atoi((char*)args)); - int32 offset = area / 32; - uint32 val = uint32((1 << (area % 32))); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(atoi(args)); + if (!area) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } - if (area<0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + int32 offset = area->exploreFlag / 32; + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } + uint32 val = uint32((1 << (area->exploreFlag % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields | val))); @@ -1442,17 +1448,23 @@ public: return false; } - int32 area = GetAreaFlagByAreaID(atoi((char*)args)); - int32 offset = area / 32; - uint32 val = uint32((1 << (area % 32))); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(atoi(args)); + if (!area) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } - if (area < 0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + int32 offset = area->exploreFlag / 32; + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } + uint32 val = uint32((1 << (area->exploreFlag % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields ^ val))); @@ -2025,12 +2037,12 @@ public: MapEntry const* map = sMapStore.LookupEntry(mapId); - AreaTableEntry const* area = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId); if (area) { areaName = area->area_name[locale]; - AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone); if (zone) zoneName = zone->area_name[locale]; } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 075eae219c..928569fbc7 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -668,6 +668,7 @@ public: stmt->setFloat(5, player->GetPositionY()); stmt->setFloat(6, player->GetPositionZ()); stmt->setFloat(7, distance * distance); + stmt->setFloat(8, player->GetPhaseMask()); PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) diff --git a/src/server/scripts/Custom/README.md b/src/server/scripts/Custom/README.md index 7b9e83d4e0..05944b8706 100644 --- a/src/server/scripts/Custom/README.md +++ b/src/server/scripts/Custom/README.md @@ -6,14 +6,20 @@ They will be git-ignored Remember to use cmake macro inside your CMakeLists.txt to correctly add scripts to project solution. -BTW, **We strongly suggest you** to use our module system to create your custom + + +/!\ BTW, **We strongly suggest you** to use our module system to create your custom powerful module instead of simple scripts. ----------------- -CMakeLists.txt example: +How to: + +1) Create a CMakeLists.txt in this directory + +Example (everything below is needed, just replace with your scripts' names): set(scripts_STAT_SRCS ${scripts_STAT_SRCS} @@ -24,3 +30,8 @@ set(scripts_STAT_SRCS AC_ADD_SCRIPT_LOADER("Custom" "ScriptLoader.h") message(" -> Prepared: My custom scripts") + + +2) Add them to ../ScriptLoader.cpp + +Open the file and go at the end of the file to know what to edit. diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h index fd073b5a74..4073401b96 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h @@ -110,4 +110,9 @@ enum GameObjectsIds GO_UROK_PILE = 175621, }; +enum npcspells +{ + SPELL_FINKLE_IS_EINHORN = 16710 +}; + #endif diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index 751f58635b..98bd6fbec6 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -67,6 +67,19 @@ public: memset(go_emberseerrunes, 0, sizeof(go_emberseerrunes)); } + void CreatureLooted(Creature* creature, LootType loot) + { + switch (creature->GetEntry()) + { + case NPC_THE_BEAST: + if (loot == LOOT_SKINNING) + { + creature->CastSpell(creature, SPELL_FINKLE_IS_EINHORN, true); + } + break; + } + } + void OnCreatureCreate(Creature* creature) { switch (creature->GetEntry()) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp index 3c279ba2f7..5b44d39437 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp @@ -61,6 +61,17 @@ class boss_golemagg : public CreatureScript { BossAI::EnterCombat(victim); events.ScheduleEvent(EVENT_PYROBLAST, 7000); + + // The two ragers should join the fight alongside me against my foes. + std::list<Creature *> ragers; + me->GetCreaturesWithEntryInRange(ragers, 100, NPC_CORE_RAGER); + for (Creature * i : ragers) + { + if (i && i->IsAlive() && !i->IsInCombat()) + { + i->AI()->AttackStart(victim); + } + } } void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp index 1b515591c7..0908b5b107 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_magmadar.cpp @@ -4,12 +4,12 @@ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> */ -/* ScriptData -SDName: Boss_Magmadar -SD%Complete: 75 -SDComment: Conflag on ground nyi -SDCategory: Molten Core -EndScriptData */ + /* ScriptData + SDName: Boss_Magmadar + SD%Complete: 75 + SDComment: Conflag on ground nyi + SDCategory: Molten Core + EndScriptData */ #include "ObjectMgr.h" #include "ScriptMgr.h" @@ -18,93 +18,229 @@ EndScriptData */ enum Texts { - EMOTE_FRENZY = 0 + EMOTE_FRENZY = 0, + EMOTE_SMOLDERING = 0, + EMOTE_IGNITE = 1, }; enum Spells { - SPELL_FRENZY = 19451, - SPELL_MAGMA_SPIT = 19449, - SPELL_PANIC = 19408, - SPELL_LAVA_BOMB = 19428, + SPELL_FRENZY = 19451, + SPELL_MAGMA_SPIT = 19449, + SPELL_PANIC = 19408, + SPELL_LAVA_BOMB = 19428, + SPELL_SERRATED_BITE = 19771, }; enum Events { - EVENT_FRENZY = 1, - EVENT_PANIC = 2, - EVENT_LAVA_BOMB = 3, + EVENT_FRENZY = 1, + EVENT_PANIC = 2, + EVENT_LAVA_BOMB = 3, + EVENT_SERRATED_BITE = 1, + EVENT_IGNITE = 2, }; class boss_magmadar : public CreatureScript { - public: - boss_magmadar() : CreatureScript("boss_magmadar") { } +public: + boss_magmadar() : CreatureScript("boss_magmadar") { } - struct boss_magmadarAI : public BossAI + struct boss_magmadarAI : public BossAI + { + boss_magmadarAI(Creature* creature) : BossAI(creature, BOSS_MAGMADAR) { - boss_magmadarAI(Creature* creature) : BossAI(creature, BOSS_MAGMADAR) - { - } + } + + void Reset() + { + BossAI::Reset(); + DoCast(me, SPELL_MAGMA_SPIT, true); + } + + void EnterCombat(Unit* victim) + { + BossAI::EnterCombat(victim); + events.ScheduleEvent(EVENT_FRENZY, 30000); + events.ScheduleEvent(EVENT_PANIC, 20000); + events.ScheduleEvent(EVENT_LAVA_BOMB, 12000); + } + + void UpdateAI(uint32 diff) + { + if (!UpdateVictim()) + return; + + events.Update(diff); - void Reset() + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - BossAI::Reset(); - DoCast(me, SPELL_MAGMA_SPIT, true); + switch (eventId) + { + case EVENT_FRENZY: + Talk(EMOTE_FRENZY); + DoCast(me, SPELL_FRENZY); + events.ScheduleEvent(EVENT_FRENZY, 15000); + break; + case EVENT_PANIC: + DoCastVictim(SPELL_PANIC); + events.ScheduleEvent(EVENT_PANIC, 35000); + break; + case EVENT_LAVA_BOMB: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_LAVA_BOMB)) + DoCast(target, SPELL_LAVA_BOMB); + events.ScheduleEvent(EVENT_LAVA_BOMB, 12000); + break; + default: + break; + } } - void EnterCombat(Unit* victim) + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new boss_magmadarAI(creature); + } +}; + +// Serrated Bites timer may be wrong +class npc_magmadar_core_hound : public CreatureScript +{ +public: + npc_magmadar_core_hound() : CreatureScript("npc_magmadar_core_hound") { } + + struct npc_magmadar_core_houndAI : public CreatureAI + { + npc_magmadar_core_houndAI(Creature* creature) : CreatureAI(creature) + { + } + + EventMap events; + std::list<Creature *> hounds; + bool smoldering = false; + Unit* killer; + + void removeFeignDeath() { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29); + me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT); + me->ClearUnitState(UNIT_STATE_DIED); + me->ClearUnitState(UNIT_STATE_CANNOT_AUTOATTACK); + me->DisableRotate(false); + } + + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) + { + if (me->HealthBelowPctDamaged(0, damage)) { - BossAI::EnterCombat(victim); - events.ScheduleEvent(EVENT_FRENZY, 30000); - events.ScheduleEvent(EVENT_PANIC, 20000); - events.ScheduleEvent(EVENT_LAVA_BOMB, 12000); + if (!smoldering) + { + killer = attacker; + events.ScheduleEvent(EVENT_IGNITE, 10000); + me->SetHealth(1); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29); + me->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + me->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->AddUnitMovementFlag(MOVEMENTFLAG_ROOT); + me->AddUnitState(UNIT_STATE_DIED); + me->AddUnitState(UNIT_STATE_CANNOT_AUTOATTACK); + me->DisableRotate(true); + Talk(EMOTE_SMOLDERING); + } + damage = 0; + smoldering = true; } + } - void UpdateAI(uint32 diff) - { - if (!UpdateVictim()) - return; + void Reset() { + removeFeignDeath(); + } + + void JustDied(Unit* /*killer*/) { + removeFeignDeath(); + } - events.Update(diff); + void EnterCombat(Unit* /*victim*/) + { + events.ScheduleEvent(EVENT_SERRATED_BITE, 10000); // timer may be wrong + } + + void UpdateAI(uint32 diff) + { + if (!UpdateVictim() && !smoldering) + return; - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) + case EVENT_SERRATED_BITE: + if (UpdateVictim() && !smoldering) { + DoCast(me->GetVictim(), SPELL_SERRATED_BITE); + events.ScheduleEvent(EVENT_SERRATED_BITE, 10000); // again, timer may be wrong + } + break; + case EVENT_IGNITE: + smoldering = false; + me->GetCreaturesWithEntryInRange(hounds, 80, NPC_CORE_HOUND); + for (Creature * i : hounds) { - case EVENT_FRENZY: - Talk(EMOTE_FRENZY); - DoCast(me, SPELL_FRENZY); - events.ScheduleEvent(EVENT_FRENZY, 15000); - break; - case EVENT_PANIC: - DoCastVictim(SPELL_PANIC); - events.ScheduleEvent(EVENT_PANIC, 35000); - break; - case EVENT_LAVA_BOMB: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_LAVA_BOMB)) - DoCast(target, SPELL_LAVA_BOMB); - events.ScheduleEvent(EVENT_LAVA_BOMB, 12000); - break; - default: - break; + if (i && i->IsAlive() && i->IsInCombat() && !i->HasUnitState(UNIT_STATE_DIED)) + { + Talk(EMOTE_IGNITE); + me->SetFullHealth(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29); + me->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT); + me->ClearUnitState(UNIT_STATE_DIED); + me->ClearUnitState(UNIT_STATE_CANNOT_AUTOATTACK); + me->DisableRotate(false); + me->AI()->AttackStart(i->GetVictim()); + return; + } } + if (me->HasUnitState(UNIT_STATE_DIED)) + { + if (killer) + { + me->Kill(killer, me); + } + else + { + me->Kill(me, me); + } + } + break; + default: + break; } - - DoMeleeAttackIfReady(); } - }; - CreatureAI* GetAI(Creature* creature) const - { - return new boss_magmadarAI(creature); + DoMeleeAttackIfReady(); } + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_magmadar_core_houndAI(creature); + } }; void AddSC_boss_magmadar() { new boss_magmadar(); + new npc_magmadar_core_hound(); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index 357ea5dd7d..ebf5aa3a2d 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -80,6 +80,8 @@ class boss_majordomo : public CreatureScript events.ScheduleEvent(EVENT_DAMAGE_REFLECTION, 15000); events.ScheduleEvent(EVENT_BLAST_WAVE, 10000); events.ScheduleEvent(EVENT_TELEPORT, 20000); + // Call every flamewaker around him + me->CallForHelp(30); } void UpdateAI(uint32 diff) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp index c4c415b9af..dceeab6c86 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp @@ -61,6 +61,7 @@ enum Events EVENT_INTRO_5 = 12 }; + class boss_ragnaros : public CreatureScript { public: @@ -83,6 +84,7 @@ class boss_ragnaros : public CreatureScript _hasSubmergedOnce = false; _isBanished = false; me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); + me->SetOrientation(0.8f); } void EnterCombat(Unit* victim) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp index c97684c65a..8b0fba3cc9 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/instance_molten_core.cpp @@ -20,15 +20,15 @@ EndScriptData */ Position const SummonPositions[10] = { - {737.850f, -1145.35f, -120.288f, 4.71368f}, - {744.162f, -1151.63f, -119.726f, 4.58204f}, - {751.247f, -1152.82f, -119.744f, 4.49673f}, - {759.206f, -1155.09f, -120.051f, 4.30104f}, - {755.973f, -1152.33f, -120.029f, 4.25588f}, - {731.712f, -1147.56f, -120.195f, 4.95955f}, - {726.499f, -1149.80f, -120.156f, 5.24055f}, - {722.408f, -1152.41f, -120.029f, 5.33087f}, - {718.994f, -1156.36f, -119.805f, 5.75738f}, + {759.542f, -1173.43f, -118.974f, 3.3048f}, + {761.652f, -1164.30f, -119.533f, 3.3919f}, + {747.323f, -1149.24f, -120.060f, 3.6629f}, + {766.734f, -1183.16f, -119.292f, 2.9889f}, + {757.364f, -1198.31f, -118.652f, 2.3095f}, + {752.349f, -1159.19f, -119.261f, 3.6032f}, + {738.015f, -1152.22f, -119.512f, 4.0792f}, + {757.246f, -1189.79f, -118.633f, 2.5333f}, + {745.916f, -1199.35f, -118.119f, 1.8932f}, {838.510f, -829.840f, -232.000f, 2.00000f}, }; @@ -45,29 +45,17 @@ class instance_molten_core : public InstanceMapScript _golemaggTheIncineratorGUID = 0; _majordomoExecutusGUID = 0; _cacheOfTheFirelordGUID = 0; - _executusSchedule = NULL; _deadBossCount = 0; _ragnarosAddDeaths = 0; - _isLoading = false; - _summonedExecutus = false; } - ~instance_molten_core_InstanceMapScript() + void OnPlayerEnter(Player* /*player*/) override { - delete _executusSchedule; + if (CheckMajordomoExecutus()) + SummonMajordomoExecutus(); } - void OnPlayerEnter(Player* /*player*/) - { - if (_executusSchedule) - { - SummonMajordomoExecutus(*_executusSchedule); - delete _executusSchedule; - _executusSchedule = NULL; - } - } - - void OnCreatureCreate(Creature* creature) + void OnCreatureCreate(Creature* creature) override { switch (creature->GetEntry()) { @@ -82,19 +70,40 @@ class instance_molten_core : public InstanceMapScript } } - void OnGameObjectCreate(GameObject* go) + void OnGameObjectCreate(GameObject* go) override { switch (go->GetEntry()) { case GO_CACHE_OF_THE_FIRELORD: _cacheOfTheFirelordGUID = go->GetGUID(); break; + case GO_CIRCLE_BARON: + _circlesGUIDs[5] = go->GetGUID(); + break; + case GO_CIRCLE_GARR: + _circlesGUIDs[3] = go->GetGUID(); + break; + case GO_CIRCLE_GEHENNAS: + _circlesGUIDs[2] = go->GetGUID(); + break; + case GO_CIRCLE_GOLEMAGG: + _circlesGUIDs[7] = go->GetGUID(); + break; + case GO_CIRCLE_MAGMADAR: + _circlesGUIDs[1] = go->GetGUID(); + break; + case GO_CIRCLE_SHAZZRAH: + _circlesGUIDs[4] = go->GetGUID(); + break; + case GO_CIRCLE_SULFURON: + _circlesGUIDs[6] = go->GetGUID(); + break; default: break; } } - void SetData(uint32 type, uint32 data) + void SetData(uint32 type, uint32 data) override { if (type == DATA_RAGNAROS_ADDS) { @@ -105,7 +114,7 @@ class instance_molten_core : public InstanceMapScript } } - uint32 GetData(uint32 type) const + uint32 GetData(uint32 type) const override { switch (type) { @@ -116,7 +125,7 @@ class instance_molten_core : public InstanceMapScript return 0; } - uint64 GetData64(uint32 type) const + uint64 GetData64(uint32 type) const override { switch (type) { @@ -129,33 +138,28 @@ class instance_molten_core : public InstanceMapScript return 0; } - bool SetBossState(uint32 bossId, EncounterState state) + bool SetBossState(uint32 bossId, EncounterState state) override { if (!InstanceScript::SetBossState(bossId, state)) return false; - + if (state == DONE && bossId < BOSS_MAJORDOMO_EXECUTUS) - ++_deadBossCount; - - if (_isLoading) - return true; - - if (_deadBossCount == 8) - SummonMajordomoExecutus(false); - - if (bossId == BOSS_MAJORDOMO_EXECUTUS && state == DONE) + if (CheckMajordomoExecutus()) + SummonMajordomoExecutus(); + + if (bossId == BOSS_MAJORDOMO_EXECUTUS && state == DONE) { DoRespawnGameObject(_cacheOfTheFirelordGUID, 7 * DAY); + } return true; } - void SummonMajordomoExecutus(bool done) + void SummonMajordomoExecutus() { - if (_summonedExecutus) + if (_majordomoExecutusGUID) return; - _summonedExecutus = true; - if (!done) + if (GetBossState(BOSS_MAJORDOMO_EXECUTUS) != DONE) { instance->SummonCreature(NPC_MAJORDOMO_EXECUTUS, SummonPositions[0]); instance->SummonCreature(NPC_FLAMEWAKER_HEALER, SummonPositions[1]); @@ -168,10 +172,22 @@ class instance_molten_core : public InstanceMapScript instance->SummonCreature(NPC_FLAMEWAKER_ELITE, SummonPositions[8]); } else if (TempSummon* summon = instance->SummonCreature(NPC_MAJORDOMO_EXECUTUS, RagnarosTelePos)) - summon->AI()->DoAction(ACTION_START_RAGNAROS_ALT); + summon->AI()->DoAction(ACTION_START_RAGNAROS_ALT); } - std::string GetSaveData() + bool CheckMajordomoExecutus() const + { + if (GetBossState(BOSS_RAGNAROS) == DONE) + return false; + + for (uint8 i = 0; i < BOSS_MAJORDOMO_EXECUTUS; ++i) + if (GetBossState(i) != DONE) + return false; + + return true; + } + + std::string GetSaveData() override { OUT_SAVE_INST_DATA; @@ -182,7 +198,7 @@ class instance_molten_core : public InstanceMapScript return saveStream.str(); } - void Load(char const* data) + void Load(char const* data) override { if (!data) { @@ -190,7 +206,6 @@ class instance_molten_core : public InstanceMapScript return; } - _isLoading = true; OUT_LOAD_INST_DATA(data); char dataHead1, dataHead2; @@ -200,44 +215,32 @@ class instance_molten_core : public InstanceMapScript if (dataHead1 == 'M' && dataHead2 == 'C') { - EncounterState states[MAX_ENCOUNTER]; - uint8 executusCounter = 0; - - // need 2 loops to check spawning executus/ragnaros for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) { uint32 tmpState; loadStream >> tmpState; if (tmpState == IN_PROGRESS || tmpState > TO_BE_DECIDED) tmpState = NOT_STARTED; - states[i] = EncounterState(tmpState); - - if (tmpState == DONE && i < BOSS_MAJORDOMO_EXECUTUS) - ++executusCounter; - } - if (executusCounter >= 8 && states[BOSS_RAGNAROS] != DONE) - _executusSchedule = new bool(states[BOSS_MAJORDOMO_EXECUTUS] == DONE); + SetBossState(i, EncounterState(tmpState)); + } - for (uint8 i = 0; i < MAX_ENCOUNTER; ++i) - SetBossState(i, states[i]); + if (CheckMajordomoExecutus()) + SummonMajordomoExecutus(); } else OUT_LOAD_INST_DATA_FAIL; OUT_LOAD_INST_DATA_COMPLETE; - _isLoading = false; } private: uint64 _golemaggTheIncineratorGUID; uint64 _majordomoExecutusGUID; uint64 _cacheOfTheFirelordGUID; - bool* _executusSchedule; uint8 _deadBossCount; uint8 _ragnarosAddDeaths; - bool _isLoading; - bool _summonedExecutus; + std::unordered_map<uint8, uint64> _circlesGUIDs; }; InstanceScript* GetInstanceScript(InstanceMap* map) const diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h index 4c2f6ca393..ae4531435d 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/molten_core.h @@ -47,11 +47,20 @@ enum Creatures NPC_RAGNAROS = 11502, NPC_FLAMEWAKER_HEALER = 11663, NPC_FLAMEWAKER_ELITE = 11664, + NPC_CORE_RAGER = 11672, + NPC_CORE_HOUND = 11671, }; enum GameObjects { GO_CACHE_OF_THE_FIRELORD = 179703, + GO_CIRCLE_SULFURON = 178187, + GO_CIRCLE_BARON = 178188, + GO_CIRCLE_SHAZZRAH = 178189, + GO_CIRCLE_GOLEMAGG = 178190, + GO_CIRCLE_GARR = 178191, + GO_CIRCLE_MAGMADAR = 178192, + GO_CIRCLE_GEHENNAS = 178193, }; enum Data diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ayamiss.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ayamiss.cpp index 55c745a8a8..3278c123f4 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ayamiss.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ayamiss.cpp @@ -196,11 +196,13 @@ class boss_ayamiss : public CreatureScript events.ScheduleEvent(EVENT_SWARMER_ATTACK, 60000); break; case EVENT_SUMMON_SWARMER: + { Position Pos; me->GetRandomPoint(SwarmerPos, 80.0f, Pos); me->SummonCreature(NPC_SWARMER, Pos); events.ScheduleEvent(EVENT_SUMMON_SWARMER, 5000); break; + } case EVENT_TRASH: DoCastVictim(SPELL_TRASH); events.ScheduleEvent(EVENT_TRASH, urand(5000, 7000)); diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index f16be1443d..5f48069dd1 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -880,7 +880,7 @@ public: } else { - NextWaveTimer = 65000; + NextWaveTimer = 150000; uint8 num_to_activate = 5; if (WaveNumber <= 2) 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 e33efd3fa5..dd4f6b2e76 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -107,7 +107,7 @@ enum Spells SPELL_RISEN_WITCH_DOCTOR_SPAWN = 69639, SPELL_SUMMON_SHAMBLING_HORROR = 70372, SPELL_SUMMON_DRUDGE_GHOULS = 70358, - SPELL_INFEST = 70541, + SPELL_INFEST = 70541, //cast time 2 sec SPELL_NECROTIC_PLAGUE = 70337, SPELL_NECROTIC_PLAGUE_JUMP = 70338, SPELL_PLAGUE_SIPHON = 74074, @@ -116,12 +116,12 @@ enum Spells SPELL_SHADOW_TRAP_KNOCKBACK = 73529, // Phase 2 - SPELL_DEFILE = 72762, + SPELL_DEFILE = 72762, //cast time 2 sec SPELL_DEFILE_AURA = 72743, SPELL_DEFILE_GROW = 72756, - SPELL_SOUL_REAPER = 69409, + SPELL_SOUL_REAPER = 69409, // instant SPELL_SOUL_REAPER_BUFF = 69410, - SPELL_SUMMON_VALKYR = 69037, + SPELL_SUMMON_VALKYR = 69037, // instant SPELL_SUMMON_VALKYR_PERIODIC = 74361, SPELL_WINGS_OF_THE_DAMNED = 74352, SPELL_VALKYR_TARGET_SEARCH = 69030, @@ -980,10 +980,10 @@ class boss_the_lich_king : public CreatureScript case EVENT_QUAKE: _phase = PHASE_TWO; events.CancelEventGroup(EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_INFEST, 8000, EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_SUMMON_VALKYR, 15000, EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_SOUL_REAPER, 22000, EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_DEFILE, 32500, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_INFEST, 14000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_SUMMON_VALKYR, 20000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_SOUL_REAPER, 40000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_DEFILE, 38000, EVENT_GROUP_ABILITIES); me->InterruptNonMeleeSpells(false); me->ClearUnitState(UNIT_STATE_CASTING); @@ -995,10 +995,10 @@ class boss_the_lich_king : public CreatureScript case EVENT_QUAKE_2: _phase = PHASE_THREE; events.CancelEventGroup(EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_SOUL_REAPER, 25000, EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_DEFILE, 32500, EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_VILE_SPIRITS, 18000, EVENT_GROUP_VILE_SPIRITS); - events.ScheduleEvent(IsHeroic() ? EVENT_HARVEST_SOULS : EVENT_HARVEST_SOUL, 11000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_SOUL_REAPER, 40000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_DEFILE, 38000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_VILE_SPIRITS, 20000, EVENT_GROUP_VILE_SPIRITS); + events.ScheduleEvent(IsHeroic() ? EVENT_HARVEST_SOULS : EVENT_HARVEST_SOUL, 14000, EVENT_GROUP_ABILITIES); me->InterruptNonMeleeSpells(false); me->ClearUnitState(UNIT_STATE_CASTING); @@ -1020,7 +1020,7 @@ class boss_the_lich_king : public CreatureScript break; case EVENT_INFEST: me->CastSpell(me, SPELL_INFEST, false); - events.ScheduleEvent(EVENT_INFEST, urand(21000, 22000), EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_INFEST, 22500, EVENT_GROUP_ABILITIES); break; case EVENT_NECROTIC_PLAGUE: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, NecroticPlagueTargetCheck(me, NECROTIC_PLAGUE_LK, NECROTIC_PLAGUE_PLR))) @@ -1040,7 +1040,7 @@ class boss_the_lich_king : public CreatureScript case EVENT_PAIN_AND_SUFFERING: if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) { - events.DelayEventsToMax(500, EVENT_GROUP_ABILITIES); + //events.DelayEventsToMax(500, EVENT_GROUP_ABILITIES); me->SetOrientation(me->GetAngle(target)); me->CastSpell(target, SPELL_PAIN_AND_SUFFERING, false); } @@ -1058,33 +1058,43 @@ class boss_the_lich_king : public CreatureScript case EVENT_DEFILE: { uint32 evTime = events.GetNextEventTime(EVENT_SUMMON_VALKYR); - if (evTime && (events.GetTimer() > evTime || evTime - events.GetTimer() < 5000)) // defile cast 2sec -> valkyr in less than 3 secs after defile appears + // if defile (cast time 2sec) is less than 3 before valkyr appears + // we've to decide + if (evTime && (events.GetTimer() > evTime || evTime - events.GetTimer() < 5000)) { - if (events.GetTimer() > evTime || evTime - events.GetTimer() < 3500) // valkyr is less than 1.5 secs after defile - reschedule defile + // if valkyr is less than 1.5 secs after defile (cast time 2 sec) then we've a sync issue, so + // we need to cancel it (break) and schedule a defile to be casted 5 or 4 seconds after valkyr + if (events.GetTimer() > evTime || evTime - events.GetTimer() < 3500) { uint32 t = events.GetTimer() > evTime ? 0 : evTime - events.GetTimer(); events.ScheduleEvent(EVENT_DEFILE, t+(Is25ManRaid() ? 5000 : 4000), EVENT_GROUP_ABILITIES); break; - } + } + + // if valkyr is coming between 1.5 and 3 seconds after defile then we've to // delay valkyr just a bit events.RescheduleEvent(EVENT_SUMMON_VALKYR, 5000, EVENT_GROUP_ABILITIES); } + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, DefileTargetSelector(me))) { Talk(EMOTE_DEFILE_WARNING); me->CastSpell(target, SPELL_DEFILE, false); - events.ScheduleEvent(EVENT_DEFILE, urand(31000, 34000), EVENT_GROUP_ABILITIES); + // defile has a fixed CD (from dbm) that can be variable only + // if no target has been found at the moment (schedule after 1 second) + events.ScheduleEvent(EVENT_DEFILE, 32500, EVENT_GROUP_ABILITIES); + } + else { + // be sure it happen trying each seconds if no target + events.ScheduleEvent(EVENT_DEFILE, 1000, EVENT_GROUP_ABILITIES); } - else - events.ScheduleEvent(EVENT_DEFILE, 2000, EVENT_GROUP_ABILITIES); } break; case EVENT_SOUL_REAPER: if (me->IsWithinMeleeRange(me->GetVictim())) { me->CastSpell(me->GetVictim(), SPELL_SOUL_REAPER, false); - events.DelayEventsToMax(12000, EVENT_GROUP_ABILITIES); - events.ScheduleEvent(EVENT_SOUL_REAPER, 30000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_SOUL_REAPER, 30500, EVENT_GROUP_ABILITIES); } else events.ScheduleEvent(EVENT_SOUL_REAPER, 1000, EVENT_GROUP_ABILITIES); @@ -1094,12 +1104,15 @@ class boss_the_lich_king : public CreatureScript me->GetMap()->SetZoneMusic(AREA_THE_FROZEN_THRONE, MUSIC_SPECIAL); Talk(SAY_LK_SUMMON_VALKYR); me->CastSpell((Unit*)NULL, SUMMON_VALKYR, false); - events.ScheduleEvent(EVENT_SUMMON_VALKYR, urand(45000, 48000), EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_SUMMON_VALKYR, 45000, EVENT_GROUP_ABILITIES); + // schedule a defile (or reschedule it) if next defile event + // doesn't exist ( now > next defile ) or defile is coming too soon uint32 minTime = (Is25ManRaid() ? 5000 : 4000); if (uint32 evTime = events.GetNextEventTime(EVENT_DEFILE)) - if (events.GetTimer() > evTime || evTime - events.GetTimer() < minTime) + if (events.GetTimer() > evTime || evTime - events.GetTimer() < minTime) { events.RescheduleEvent(EVENT_DEFILE, minTime, EVENT_GROUP_ABILITIES); + } } break; case EVENT_VILE_SPIRITS: @@ -1112,7 +1125,7 @@ class boss_the_lich_king : public CreatureScript { Talk(SAY_LK_HARVEST_SOUL); me->CastSpell(target, SPELL_HARVEST_SOUL, false); - events.ScheduleEvent(EVENT_HARVEST_SOUL, 70000, EVENT_GROUP_ABILITIES); + events.ScheduleEvent(EVENT_HARVEST_SOUL, 75000, EVENT_GROUP_ABILITIES); } else events.ScheduleEvent(EVENT_HARVEST_SOUL, 10000, EVENT_GROUP_ABILITIES); @@ -3789,4 +3802,4 @@ void AddSC_boss_the_lich_king() new npc_lk_wicked_spirit(); new achievement_been_waiting_long_time(); new achievement_neck_deep_in_vile(); -}
\ No newline at end of file +} diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index d1adf13cd9..b6d11e8967 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -1548,7 +1548,8 @@ class instance_icecrown_citadel : public InstanceMapScript std::ostringstream saveStream; saveStream << "I C " << GetBossSaveData() << HeroicAttempts << ' ' << ColdflameJetsState << ' ' << BloodQuickeningState << ' ' << BloodQuickeningMinutes << ' ' << WeeklyQuestId10 << ' ' << PutricideEventProgress << ' ' - << uint32(LichKingHeroicAvailable ? 1 : 0) << ' ' << BloodPrinceTrashCount; + << uint32(LichKingHeroicAvailable ? 1 : 0) << ' ' << BloodPrinceTrashCount << ' ' << uint32(IsBuffAvailable ? 1 : 0); + OUT_SAVE_INST_DATA_COMPLETE; return saveStream.str(); @@ -1600,6 +1601,8 @@ class instance_icecrown_citadel : public InstanceMapScript loadStream >> temp; LichKingHeroicAvailable = temp ? true : false; loadStream >> BloodPrinceTrashCount; + loadStream >> temp; + SetData(DATA_BUFF_AVAILABLE, temp ? true : false); } else OUT_LOAD_INST_DATA_FAIL; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index 1b8f6de3e7..461ce780a9 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -49,9 +49,9 @@ public: return new boss_anubrekhanAI (pCreature); } - struct boss_anubrekhanAI : public ScriptedAI + struct boss_anubrekhanAI : public BossAI { - boss_anubrekhanAI(Creature *c) : ScriptedAI(c), summons(me) + boss_anubrekhanAI(Creature *c) : BossAI(c, BOSS_ANUB), summons(me) { pInstance = c->GetInstanceScript(); sayGreet = false; @@ -71,15 +71,15 @@ public: } } - void Reset() + void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); SummonCryptGuards(); if (pInstance) { - pInstance->SetData(EVENT_ANUB, NOT_STARTED); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_ANUB_GATE))) go->SetGoState(GO_STATE_ACTIVE); } @@ -107,13 +107,13 @@ public: void SummonedCreatureDespawn(Creature* cr) { summons.Despawn(cr); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); summons.DespawnAll(); if (pInstance) { pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - pInstance->SetData(EVENT_ANUB, DONE); } } @@ -132,13 +132,13 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->CallForHelp(30.0f); // catch helpers Talk(SAY_AGGRO); if (pInstance) { - pInstance->SetData(EVENT_ANUB, IN_PROGRESS); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_ANUB_GATE))) go->SetGoState(GO_STATE_READY); } @@ -179,12 +179,14 @@ public: events.RepeatEvent(20000); break; case EVENT_SPELL_LOCUST_SWARM: + { me->CastSpell(me, RAID_MODE(SPELL_LOCUST_SWARM_10, SPELL_LOCUST_SWARM_25), false); Position pos; - me->GetNearPosition(pos, 10.0f, rand_norm()*2*M_PI); + me->GetNearPosition(pos, 10.0f, rand_norm() * 2 * M_PI); me->SummonCreature(NPC_CRYPT_GUARD, pos); events.RepeatEvent(90000); break; + } case EVENT_SPELL_BERSERK: me->CastSpell(me, SPELL_BERSERK, true); events.PopEvent(); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp index 65545bba68..96961d1ae2 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp @@ -49,9 +49,9 @@ public: return new boss_faerlinaAI (pCreature); } - struct boss_faerlinaAI : public ScriptedAI + struct boss_faerlinaAI : public BossAI { - boss_faerlinaAI(Creature *c) : ScriptedAI(c), summons(me) + boss_faerlinaAI(Creature *c) : BossAI(c, BOSS_FAERLINA), summons(me) { pInstance = me->GetInstanceScript(); sayGreet = false; @@ -81,24 +81,21 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); SummonHelpers(); - if (pInstance) - pInstance->SetData(EVENT_FAERLINA, NOT_STARTED); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { - me->SetInCombatWithZone(); + BossAI::EnterCombat(who); + summons.DoZoneInCombat(); Talk(SAY_AGGRO); events.ScheduleEvent(EVENT_SPELL_POISON_BOLT, urand(12000,15000)); events.ScheduleEvent(EVENT_SPELL_RAIN_OF_FIRE, urand(6000,18000)); events.ScheduleEvent(EVENT_SPELL_FRENZY, urand(60000,80000), 1); events.SetPhase(1); - - if (pInstance) - pInstance->SetData(EVENT_FAERLINA, IN_PROGRESS); } void MoveInLineOfSight(Unit *who) @@ -124,11 +121,10 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(EVENT_FAERLINA, DONE); } void UpdateAI(uint32 diff) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp index 36c8b29979..21cff6286e 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp @@ -121,9 +121,9 @@ public: return new boss_four_horsemenAI (pCreature); } - struct boss_four_horsemenAI : public ScriptedAI + struct boss_four_horsemenAI : public BossAI { - boss_four_horsemenAI(Creature *c) : ScriptedAI(c) + boss_four_horsemenAI(Creature *c) : BossAI(c, BOSS_HORSEMAN) { pInstance = me->GetInstanceScript(); switch (me->GetEntry()) @@ -174,15 +174,13 @@ public: void Reset() { + BossAI::Reset(); me->SetPosition(me->GetHomePosition()); movementPhase = MOVE_PHASE_NONE; currentWaypoint = 0; me->SetReactState(REACT_AGGRESSIVE); events.Reset(); - if (pInstance) - pInstance->SetData(EVENT_HORSEMAN, NOT_STARTED); - // Schedule Events events.RescheduleEvent(EVENT_SPELL_MARK_CAST, 24000); events.RescheduleEvent(EVENT_BERSERK, 100*15000); @@ -248,25 +246,24 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); if (pInstance) { - pInstance->SetData(EVENT_HORSEMAN, DONE); - if (pInstance->GetData(EVENT_HORSEMAN) == DONE) + if (pInstance->GetBossState(BOSS_HORSEMAN) == DONE) { if (!me->GetMap()->GetPlayers().isEmpty()) if (Player* player = me->GetMap()->GetPlayers().getFirst()->GetSource()) player->SummonGameObject(RAID_MODE(GO_HORSEMEN_CHEST_10, GO_HORSEMEN_CHEST_25), 2514.8f, -2944.9f, 245.55f, 5.51f, 0, 0, 0, 0, 0); + } } Talk(SAY_DEATH); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { - if (pInstance) - pInstance->SetData(EVENT_HORSEMAN, IN_PROGRESS); - + BossAI::EnterCombat(who); if (movementPhase == MOVE_PHASE_NONE) { Talk(SAY_AGGRO); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp index 5a39e88fef..fdc4a5e6a8 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp @@ -52,9 +52,9 @@ public: return new boss_gluthAI (pCreature); } - struct boss_gluthAI : public ScriptedAI + struct boss_gluthAI : public BossAI { - boss_gluthAI(Creature *c) : ScriptedAI(c), summons(me) + boss_gluthAI(Creature *c) : BossAI(c, BOSS_GLUTH), summons(me) { pInstance = me->GetInstanceScript(); } @@ -66,14 +66,12 @@ public: void Reset() { + BossAI::Reset(); me->ApplySpellImmune(29306, IMMUNITY_ID, 29306, true); events.Reset(); summons.DespawnAll(); gazeTarget = 0; me->SetReactState(REACT_AGGRESSIVE); - - if (pInstance) - pInstance->SetData(EVENT_GLUTH, NOT_STARTED); } void MoveInLineOfSight(Unit *who) @@ -87,8 +85,9 @@ public: ScriptedAI::MoveInLineOfSight(who); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_SPELL_MORTAL_WOUND, 10000); events.ScheduleEvent(EVENT_SPELL_ENRAGE, 30000); @@ -96,9 +95,6 @@ public: events.ScheduleEvent(EVENT_SPELL_BERSERK, 8*60000); events.ScheduleEvent(EVENT_SUMMON_ZOMBIE, 10000); events.ScheduleEvent(EVENT_CAN_EAT_ZOMBIE, 1000); - - if (pInstance) - pInstance->SetData(EVENT_GLUTH, IN_PROGRESS); } void JustSummoned(Creature *summon) @@ -120,11 +116,10 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit*) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); summons.DespawnAll(); - if (pInstance) - pInstance->SetData(EVENT_GLUTH, DONE); } bool SelectPlayerInRoom() diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index 10330f28ab..897103e475 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -135,9 +135,9 @@ public: return new boss_gothikAI (pCreature); } - struct boss_gothikAI : public ScriptedAI + struct boss_gothikAI : public BossAI { - boss_gothikAI(Creature *c) : ScriptedAI(c), summons(me) + boss_gothikAI(Creature *c) : BossAI(c, BOSS_GOTHIK), summons(me) { pInstance = me->GetInstanceScript(); } @@ -162,6 +162,7 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_IMMUNE_TO_PC|UNIT_FLAG_DISABLE_MOVE); @@ -172,7 +173,6 @@ public: if (pInstance) { - pInstance->SetData(EVENT_GOTHIK, NOT_STARTED); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_GOTHIK_ENTER_GATE))) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_GOTHIK_INNER_GATE))) @@ -182,8 +182,9 @@ public: } } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->SetInCombatWithZone(); Talk(SAY_SPEECH); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); @@ -194,7 +195,6 @@ public: if (pInstance) { - pInstance->SetData(EVENT_GOTHIK, IN_PROGRESS); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_GOTHIK_ENTER_GATE))) go->SetGoState(GO_STATE_READY); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_GOTHIK_INNER_GATE))) @@ -225,14 +225,14 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); Talk(SAY_DEATH); summons.DespawnAll(); if (pInstance) { - pInstance->SetData(EVENT_GOTHIK, DONE); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_GOTHIK_ENTER_GATE))) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_GOTHIK_INNER_GATE))) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp index e0ab0e9a58..aa8b87531b 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_grobbulus.cpp @@ -45,9 +45,9 @@ public: return new boss_grobbulusAI (pCreature); } - struct boss_grobbulusAI : public ScriptedAI + struct boss_grobbulusAI : public BossAI { - boss_grobbulusAI(Creature *c) : ScriptedAI(c), summons(me) + boss_grobbulusAI(Creature *c) : BossAI(c, BOSS_GROBBULUS), summons(me) { pInstance = me->GetInstanceScript(); } @@ -59,24 +59,20 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); dropSludgeTimer = 0; - - if (pInstance) - pInstance->SetData(EVENT_GROBBULUS, NOT_STARTED); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_SPELL_POISON_CLOUD, 15000); events.ScheduleEvent(EVENT_SPELL_MUTATING_INJECTION, 20000); events.ScheduleEvent(EVENT_SPELL_SLIME_SPRAY, 10000); events.ScheduleEvent(EVENT_SPELL_BERSERK, RAID_MODE(12*MINUTE*IN_MILLISECONDS, 9*MINUTE*IN_MILLISECONDS)); - - if (pInstance) - pInstance->SetData(EVENT_GROBBULUS, IN_PROGRESS); } void SpellHitTarget(Unit *target, const SpellInfo* spellInfo) @@ -95,11 +91,10 @@ public: void SummonedCreatureDespawn(Creature* summon){ summons.Despawn(summon); } - void JustDied(Unit*) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); summons.DespawnAll(); - if (pInstance) - pInstance->SetData(EVENT_GROBBULUS, DONE); } void KilledUnit(Unit* who) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp index 1c7461c92c..c6039bac60 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp @@ -48,9 +48,9 @@ public: return new boss_heiganAI (pCreature); } - struct boss_heiganAI : public ScriptedAI + struct boss_heiganAI : public BossAI { - boss_heiganAI(Creature *c) : ScriptedAI(c) + boss_heiganAI(Creature *c) : BossAI(c, BOSS_HEIGAN) { pInstance = me->GetInstanceScript(); } @@ -63,6 +63,7 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); currentPhase = 0; currentSection = 3; @@ -70,7 +71,6 @@ public: if (pInstance) { - pInstance->SetData(EVENT_HEIGAN, NOT_STARTED); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_HEIGAN_ENTER_GATE))) go->SetGoState(GO_STATE_ACTIVE); } @@ -88,20 +88,19 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(EVENT_HEIGAN, DONE); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->SetInCombatWithZone(); Talk(SAY_AGGRO); if (pInstance) { - pInstance->SetData(EVENT_HEIGAN, IN_PROGRESS); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_HEIGAN_ENTER_GATE))) go->SetGoState(GO_STATE_READY); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index 0e9f2148f8..5af8fbd6aa 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -115,9 +115,9 @@ public: return new boss_kelthuzadAI (pCreature); } - struct boss_kelthuzadAI : public ScriptedAI + struct boss_kelthuzadAI : public BossAI { - boss_kelthuzadAI(Creature* c) : ScriptedAI(c), summons(me) + boss_kelthuzadAI(Creature* c) : BossAI(c, BOSS_KELTHUZAD), summons(me) { pInstance = me->GetInstanceScript(); } @@ -162,6 +162,7 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_DISABLE_MOVE); @@ -169,7 +170,6 @@ public: if (pInstance) { - pInstance->SetData(EVENT_KELTHUZAD, NOT_STARTED); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_KELTHUZAD_FLOOR))) { go->SetPhaseMask(1, true); @@ -198,12 +198,11 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); summons.DespawnAll(); Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(EVENT_KELTHUZAD, DONE); } void MoveInLineOfSight(Unit* who) @@ -212,8 +211,9 @@ public: AttackStart(who); } - void EnterCombat(Unit* /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); Talk(SAY_SUMMON_MINIONS); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); me->RemoveAllAttackers(); @@ -229,7 +229,6 @@ public: events.ScheduleEvent(EVENT_START_SECOND_PHASE, 228000); if (pInstance) { - pInstance->SetData(EVENT_KELTHUZAD, IN_PROGRESS); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_KELTHUZAD_FLOOR))) { events.ScheduleEvent(EVENT_FLOOR_CHANGE, 15000); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp index dca14d834f..165ff01329 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_loatheb.cpp @@ -42,9 +42,9 @@ public: return new boss_loathebAI (pCreature); } - struct boss_loathebAI : public ScriptedAI + struct boss_loathebAI : public BossAI { - boss_loathebAI(Creature *c) : ScriptedAI(c) + boss_loathebAI(Creature *c) : BossAI(c, BOSS_LOATHEB) { pInstance = me->GetInstanceScript(); } @@ -54,10 +54,10 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); if (pInstance) { - pInstance->SetData(EVENT_LOATHEB, NOT_STARTED); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_LOATHEB_GATE))) go->SetGoState(GO_STATE_ACTIVE); } @@ -77,11 +77,11 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); if (pInstance) { - pInstance->SetData(EVENT_LOATHEB, IN_PROGRESS); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_LOATHEB_GATE))) go->SetGoState(GO_STATE_READY); } @@ -93,12 +93,6 @@ public: events.ScheduleEvent(EVENT_SPELL_BERSERK, 720000); } - void JustDied(Unit* /*Killer*/) - { - if (pInstance) - pInstance->SetData(EVENT_LOATHEB, DONE); - } - void UpdateAI(uint32 diff) { if (!UpdateVictim()) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp index ad9515e9de..f93e5758cb 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp @@ -53,9 +53,9 @@ public: return new boss_maexxnaAI (pCreature); } - struct boss_maexxnaAI : public ScriptedAI + struct boss_maexxnaAI : public BossAI { - boss_maexxnaAI(Creature *c) : ScriptedAI(c), summons(me) + boss_maexxnaAI(Creature *c) : BossAI(c, BOSS_MAEXXNA), summons(me) { pInstance = me->GetInstanceScript(); } @@ -77,20 +77,21 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); if (pInstance) { - pInstance->SetData(EVENT_MAEXXNA, NOT_STARTED); - if (pInstance->GetData(EVENT_FAERLINA) == DONE) + if (pInstance->GetData(BOSS_FAERLINA) == DONE) if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_MAEXXNA_GATE))) go->SetGoState(GO_STATE_ACTIVE); } } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_WEB_WRAP, 20000); events.ScheduleEvent(EVENT_SPELL_WEB_SPRAY, 40000); @@ -101,18 +102,11 @@ public: if (pInstance) { - pInstance->SetData(EVENT_MAEXXNA, IN_PROGRESS); if (GameObject* go = me->GetMap()->GetGameObject(pInstance->GetData64(DATA_MAEXXNA_GATE))) go->SetGoState(GO_STATE_READY); } } - void JustDied(Unit* /*Killer*/) - { - if (pInstance) - pInstance->SetData(EVENT_MAEXXNA, DONE); - } - void JustSummoned(Creature* cr) { if (cr->GetEntry() == NPC_MAEXXNA_SPIDERLING) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index aa0cdeada6..95f81ca99b 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -66,9 +66,9 @@ public: return new boss_nothAI (pCreature); } - struct boss_nothAI : public ScriptedAI + struct boss_nothAI : public BossAI { - boss_nothAI(Creature *c) : ScriptedAI(c), summons(me) + boss_nothAI(Creature *c) : BossAI(c, BOSS_NOTH), summons(me) { pInstance = me->GetInstanceScript(); } @@ -123,14 +123,12 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); me->SetControlled(false, UNIT_STATE_ROOT); me->SetReactState(REACT_AGGRESSIVE); events.SetPhase(0); - - if (pInstance) - pInstance->SetData(EVENT_NOTH, NOT_STARTED); } void EnterEvadeMode() @@ -139,12 +137,10 @@ public: ScriptedAI::EnterEvadeMode(); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); Talk(SAY_AGGRO); - if (pInstance) - pInstance->SetData(EVENT_NOTH, IN_PROGRESS); - StartGroundPhase(); } @@ -154,11 +150,10 @@ public: summon->SetInCombatWithZone(); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(EVENT_NOTH, DONE); } void KilledUnit(Unit* who) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index 05b5b4649a..18910bbcd3 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -47,9 +47,9 @@ public: return new boss_patchwerkAI (pCreature); } - struct boss_patchwerkAI : public ScriptedAI + struct boss_patchwerkAI : public BossAI { - boss_patchwerkAI(Creature *c) : ScriptedAI(c) + boss_patchwerkAI(Creature *c) : BossAI(c, BOSS_PATCHWERK) { pInstance = me->GetInstanceScript(); } @@ -59,9 +59,8 @@ public: void Reset() { + BossAI::Reset(); events.Reset(); - if (pInstance) - pInstance->SetData(EVENT_PATCHWERK, NOT_STARTED); } void KilledUnit(Unit* who) @@ -76,15 +75,15 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); Talk(SAY_DEATH); - if (pInstance) - pInstance->SetData(EVENT_PATCHWERK, DONE); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); Talk(SAY_AGGRO); me->SetInCombatWithZone(); @@ -95,7 +94,6 @@ public: if (pInstance) { pInstance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - pInstance->SetData(EVENT_PATCHWERK, IN_PROGRESS); } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp index 418fae7324..def339cf5e 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp @@ -60,9 +60,9 @@ public: return new boss_razuviousAI (pCreature); } - struct boss_razuviousAI : public ScriptedAI + struct boss_razuviousAI : public BossAI { - boss_razuviousAI(Creature *c) : ScriptedAI(c), summons(me) + boss_razuviousAI(Creature *c) : BossAI(c, BOSS_RAZUVIOUS), summons(me) { pInstance = me->GetInstanceScript(); } @@ -86,11 +86,10 @@ public: void Reset() { + BossAI::Reset(); summons.DespawnAll(); events.Reset(); SpawnHelpers(); - if (pInstance) - pInstance->SetData(EVENT_RAZUVIOUS, NOT_STARTED); } void KilledUnit(Unit* who) @@ -115,18 +114,18 @@ public: me->LowerPlayerDamageReq(damage); } - void JustDied(Unit* /*killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); DoPlaySoundToSet(me, SOUND_DEATH); me->MonsterYell("An honorable... death...", LANG_UNIVERSAL, 0); me->CastSpell(me, SPELL_HOPELESS, true); - if (pInstance) - pInstance->SetData(EVENT_RAZUVIOUS, DONE); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); switch (urand(0,2)) { case 0: @@ -147,8 +146,6 @@ public: events.ScheduleEvent(EVENT_SPELL_DISRUPTING_SHOUT, 25000); events.ScheduleEvent(EVENT_SPELL_JAGGED_KNIFE, 15000); events.ScheduleEvent(EVENT_PLAY_COMMAND, 40000); - if (pInstance) - pInstance->SetData(EVENT_RAZUVIOUS, IN_PROGRESS); summons.DoZoneInCombat(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp index d0b8bb5d15..2514682c95 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp @@ -77,9 +77,9 @@ public: return new boss_sapphironAI (pCreature); } - struct boss_sapphironAI : public ScriptedAI + struct boss_sapphironAI : public BossAI { - boss_sapphironAI(Creature* c) : ScriptedAI(c) + boss_sapphironAI(Creature* c) : BossAI(c, BOSS_SAPPHIRON) { pInstance = me->GetInstanceScript(); } @@ -113,6 +113,7 @@ public: void Reset() { + BossAI::Reset(); if (me->IsVisible()) me->SetReactState(REACT_AGGRESSIVE); @@ -121,9 +122,6 @@ public: spawnTimer = 0; currentTarget = 0; blockList.clear(); - - if (pInstance) - pInstance->SetData(EVENT_SAPPHIRON, NOT_STARTED); } void EnterCombatSelfFunction() @@ -147,8 +145,9 @@ public: } } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); EnterCombatSelfFunction(); me->CastSpell(me, RAID_MODE(SPELL_FROST_AURA_10, SPELL_FROST_AURA_25), true); @@ -159,16 +158,12 @@ public: events.ScheduleEvent(EVENT_SPELL_BLIZZARD, 21000); events.ScheduleEvent(EVENT_FLIGHT_START, 45000); events.ScheduleEvent(EVENT_HUNDRED_CLUB, 5000); - - if (pInstance) - pInstance->SetData(EVENT_SAPPHIRON, IN_PROGRESS); } - void JustDied(Unit* /*who*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); me->CastSpell(me, SPELL_SAPPHIRON_DIES, true); - if (pInstance) - pInstance->SetData(EVENT_SAPPHIRON, DONE); } void DoAction(int32 param) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index 0e5648f779..51e8f5b6ca 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -94,9 +94,9 @@ public: return new boss_thaddiusAI (pCreature); } - struct boss_thaddiusAI : public ScriptedAI + struct boss_thaddiusAI : public BossAI { - boss_thaddiusAI(Creature *c) : ScriptedAI(c), summons(me) + boss_thaddiusAI(Creature *c) : BossAI(c, BOSS_THADDIUS), summons(me) { pInstance = me->GetInstanceScript(); } @@ -139,8 +139,9 @@ public: } } - void Reset() + void Reset() { + BossAI::Reset(); events.Reset(); summons.DespawnAll(); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); @@ -149,9 +150,6 @@ public: resetTimer = 1; me->SetPosition(me->GetHomePosition()); - if (pInstance) - pInstance->SetData(EVENT_THADDIUS, NOT_STARTED); - me->SummonCreature(NPC_STALAGG, 3450.45f, -2931.42f, 312.091f, 5.49779f); me->SummonCreature(NPC_FEUGEN, 3508.14f, -2988.65f, 312.092f, 2.37365f); if (Creature* cr = me->SummonCreature(NPC_TESLA_COIL, 3527.34f, -2951.56f, 318.75f, 0.0f)) @@ -182,12 +180,12 @@ public: pInstance->SetData(DATA_IMMORTAL_FAIL, 0); } - void JustDied(Unit* /*Killer*/) + void JustDied(Unit* killer) { + BossAI::JustDied(killer); Talk(SAY_DEATH); if (pInstance) { - pInstance->SetData(EVENT_THADDIUS, DONE); pInstance->DoRemoveAurasDueToSpellOnPlayers(28059); pInstance->DoRemoveAurasDueToSpellOnPlayers(28084); } @@ -195,14 +193,12 @@ public: void JustSummoned(Creature* cr) { summons.Summon(cr); } - void EnterCombat(Unit * /*who*/) + void EnterCombat(Unit * who) { + BossAI::EnterCombat(who); me->SetInCombatWithZone(); summons.DoZoneInCombat(NPC_FEUGEN); summons.DoZoneInCombat(NPC_STALAGG); - - if (pInstance) - pInstance->SetData(EVENT_THADDIUS, IN_PROGRESS); } void UpdateAI(uint32 diff) diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index d9017164cd..93db29caea 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -49,7 +49,7 @@ public: { instance_naxxramas_InstanceMapScript(Map* pMap) : InstanceScript(pMap) { - memset(&Encounters, 0, sizeof(Encounters)); + SetBossNumber(MAX_ENCOUNTERS); for (uint8 i = 0; i < 4; ++i) HeiganEruption[i].clear(); @@ -101,7 +101,6 @@ public: immortalAchievement = 1; } - uint32 Encounters[MAX_ENCOUNTERS]; std::set<GameObject*> HeiganEruption[4]; // GOs @@ -169,17 +168,17 @@ public: } } - bool IsEncounterInProgress() const + bool IsEncounterInProgress() const override { for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) { - if (Encounters[i] == IN_PROGRESS) + if (GetBossState(i) == IN_PROGRESS) return true; } return false; } - void OnCreatureCreate(Creature* creature) + void OnCreatureCreate(Creature* creature) override { switch(creature->GetEntry()) { @@ -216,7 +215,7 @@ public: } } - void OnGameObjectCreate(GameObject* pGo) + void OnGameObjectCreate(GameObject* pGo) override { if (pGo->GetGOInfo()->displayId == 6785 || pGo->GetGOInfo()->displayId == 1287) { @@ -228,57 +227,57 @@ public: { case GO_PATCHWERK_GATE: _patchwerkGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_PATCHWERK] == DONE) + if (GetBossState(BOSS_PATCHWERK) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_GLUTH_GATE: _gluthGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_GLUTH] == DONE) + if (GetBossState(BOSS_GLUTH) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_NOTH_GATE: _nothGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_NOTH] == DONE) + if (GetBossState(BOSS_NOTH) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_HEIGAN_ENTERANCE_GATE: _heiganGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_HEIGAN] == DONE || Encounters[EVENT_NOTH] == DONE) + if (GetBossState(BOSS_HEIGAN) == DONE || GetBossState(BOSS_NOTH) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_HEIGAN_EXIT_GATE: _heiganGateExitGUID = pGo->GetGUID(); - if (Encounters[EVENT_HEIGAN] == DONE) + if (GetBossState(BOSS_HEIGAN) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_LOATHEB_GATE: _loathebGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_LOATHEB] == DONE) + if (GetBossState(BOSS_LOATHEB) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_ANUB_GATE: _anubGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_ANUB] == DONE) + if (GetBossState(BOSS_ANUB) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_ANUB_NEXT_GATE: _anubNextGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_ANUB] == DONE) + if (GetBossState(BOSS_ANUB) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_FAERLINA_GATE: _faerlinaGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_FAERLINA] == DONE) + if (GetBossState(BOSS_FAERLINA) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_MAEXXNA_GATE: _maexxnaGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_FAERLINA] == DONE) // faerlina is correct + if (GetBossState(BOSS_FAERLINA) == DONE) // faerlina is correct pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_THADDIUS_GATE: _thaddiusGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_GLUTH] == DONE) // gluth is correct + if (GetBossState(BOSS_GLUTH) == DONE) // gluth is correct pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_GOTHIK_ENTER_GATE: @@ -289,12 +288,12 @@ public: break; case GO_GOTHIK_EXIT_GATE: _gothikExitGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_GOTHIK] == DONE) + if (GetBossState(BOSS_GOTHIK) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_HORSEMAN_GATE: _horsemanGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_GOTHIK] == DONE) // correct + if (GetBossState(BOSS_GOTHIK) == DONE) // correct pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_KELTHUZAD_FLOOR: @@ -305,33 +304,33 @@ public: break; case GO_SAPPHIRON_GATE: _sapphironGateGUID = pGo->GetGUID(); - if (Encounters[EVENT_SAPPHIRON] == DONE) + if (GetBossState(BOSS_SAPPHIRON) == DONE) pGo->SetGoState(GO_STATE_ACTIVE); break; case GO_DEATHKNIGHT_WING: _loathebPortalGUID = pGo->GetGUID(); - if (Encounters[EVENT_LOATHEB] == DONE) + if (GetBossState(BOSS_LOATHEB) == DONE) pGo->SetPhaseMask(1, true); break; case GO_THADDIUS_PORTAL: _thaddiusPortalGUID = pGo->GetGUID(); - if (Encounters[EVENT_THADDIUS] == DONE) + if (GetBossState(BOSS_THADDIUS) == DONE) pGo->SetPhaseMask(1, true); break; case GO_MAEXXNA_PORTAL: _maexxnaPortalGUID = pGo->GetGUID(); - if (Encounters[EVENT_MAEXXNA] == DONE) + if (GetBossState(BOSS_MAEXXNA) == DONE) pGo->SetPhaseMask(1, true); break; case GO_HORSEMAN_PORTAL: _horsemanPortalGUID = pGo->GetGUID(); - if (Encounters[EVENT_HORSEMAN] == DONE) + if (GetBossState(BOSS_HORSEMAN) == DONE) pGo->SetPhaseMask(1, true); break; } } - void OnGameObjectRemove(GameObject* pGo) + void OnGameObjectRemove(GameObject* pGo) override { if (pGo->GetGOInfo()->displayId == 6785 || pGo->GetGOInfo()->displayId == 1287) { @@ -345,7 +344,7 @@ public: cr->AI()->DoAction(ACTION_SAPPHIRON_BIRTH); } - bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) + bool CheckAchievementCriteriaMeet(uint32 criteria_id, Player const* /*source*/, Unit const* /*target*/, uint32 /*miscvalue1*/) override { switch(criteria_id) { @@ -389,7 +388,7 @@ public: { uint8 count = 0; for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) - if (Encounters[i] == NOT_STARTED) + if (GetBossState(i) == NOT_STARTED) ++count; return !count && immortalAchievement; @@ -398,57 +397,10 @@ public: return false; } - void SetData(uint32 id, uint32 data) + void SetData(uint32 id, uint32 data) override { - // Bosses data switch(id) { - case EVENT_PATCHWERK: - case EVENT_GROBBULUS: - case EVENT_GLUTH: - case EVENT_NOTH: - case EVENT_ANUB: - case EVENT_MAEXXNA: - case EVENT_RAZUVIOUS: - case EVENT_GOTHIK: - // EVENT_HORSEMAN HANDLED BELOW - Encounters[id] = data; - break; - case EVENT_KELTHUZAD: - if (data == NOT_STARTED) - abominationsKilled = 0; - Encounters[id] = data; - break; - case EVENT_FAERLINA: - if (data == NOT_STARTED) - faerlinaAchievement = true; - Encounters[id] = data; - break; - case EVENT_THADDIUS: - if (data == NOT_STARTED) - thaddiusAchievement = true; - Encounters[id] = data; - break; - case EVENT_LOATHEB: - if (data == NOT_STARTED) - loathebAchievement = true; - Encounters[id] = data; - break; - case EVENT_HEIGAN: - if (data == NOT_STARTED) - heiganAchievement = true; - Encounters[id] = data; - break; - case DATA_HEIGAN_ERUPTION: - HeiganEruptSections(data); - return; - case EVENT_SAPPHIRON: - Encounters[id] = data; - if (data == DONE) - _speakTimer = 1; - else if (data == NOT_STARTED) - sapphironAchievement = true; - break; case DATA_ABOMINATION_KILLED: abominationsKilled++; return; @@ -471,17 +423,24 @@ public: immortalAchievement = 0; SaveToDB(); return; + case DATA_HEIGAN_ERUPTION: + HeiganEruptSections(data); + return; } - + } + + bool SetBossState(uint32 bossId, EncounterState state) override + { // Horseman handling - if (id == EVENT_HORSEMAN) + if (bossId == BOSS_HORSEMAN) { - if (data == DONE) + if (state == DONE) { _horsemanTimer++; _horsemanKilled++; - if (_horsemanKilled < 4) - return; + if (_horsemanKilled < 4) { + return false; + } // All horsemans are killed if (Creature* cr = instance->GetCreature(_blaumeuxGUID)) @@ -489,7 +448,7 @@ public: } // respawn - else if (data == NOT_STARTED && _horsemanKilled > 0) + else if (state == NOT_STARTED && _horsemanKilled > 0) { Creature* cr; _horsemanKilled = 0; @@ -518,7 +477,7 @@ public: cr->Respawn(); } } - else if (data == IN_PROGRESS) + else if (state == IN_PROGRESS) { Creature* cr; if ((cr = instance->GetCreature(_blaumeuxGUID))) @@ -531,66 +490,99 @@ public: cr->SetInCombatWithZone(); } - if (data == NOT_STARTED) + if (state == NOT_STARTED) _horsemanTimer = 0; - - Encounters[id] = data; } - + + + if (!InstanceScript::SetBossState(bossId, state)) + return false; + + // Bosses data + switch(bossId) + { + case BOSS_KELTHUZAD: + if (state == NOT_STARTED) + abominationsKilled = 0; + break; + case BOSS_FAERLINA: + if (state == NOT_STARTED) + faerlinaAchievement = true; + break; + case BOSS_THADDIUS: + if (state == NOT_STARTED) + thaddiusAchievement = true; + break; + case BOSS_LOATHEB: + if (state == NOT_STARTED) + loathebAchievement = true; + break; + case BOSS_HEIGAN: + if (state == NOT_STARTED) + heiganAchievement = true; + break; + case BOSS_SAPPHIRON: + if (state == DONE) + _speakTimer = 1; + else if (state == NOT_STARTED) + sapphironAchievement = true; + break; + } + // Save instance and open gates - if (data == DONE) + if (state == DONE) { SaveToDB(); - switch (id) + switch (bossId) { - case EVENT_PATCHWERK: + case BOSS_PATCHWERK: if (GameObject* go = instance->GetGameObject(_patchwerkGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_GLUTH: + case BOSS_GLUTH: if (GameObject* go = instance->GetGameObject(_gluthGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_thaddiusGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_NOTH: + case BOSS_NOTH: if (GameObject* go = instance->GetGameObject(_nothGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_heiganGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_HEIGAN: + case BOSS_HEIGAN: if (GameObject* go = instance->GetGameObject(_heiganGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_heiganGateExitGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_LOATHEB: + case BOSS_LOATHEB: if (GameObject* go = instance->GetGameObject(_loathebGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_loathebPortalGUID)) go->SetPhaseMask(1, true); break; - case EVENT_ANUB: + case BOSS_ANUB: if (GameObject* go = instance->GetGameObject(_anubGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_anubNextGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_FAERLINA: + case BOSS_FAERLINA: if (GameObject* go = instance->GetGameObject(_faerlinaGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_maexxnaGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_MAEXXNA: + case BOSS_MAEXXNA: if (GameObject* go = instance->GetGameObject(_maexxnaGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_maexxnaPortalGUID)) go->SetPhaseMask(1, true); break; - case EVENT_GOTHIK: + case BOSS_GOTHIK: if (GameObject* go = instance->GetGameObject(_gothikEnterGateGUID)) go->SetGoState(GO_STATE_ACTIVE); if (GameObject* go = instance->GetGameObject(_gothikExitGateGUID)) @@ -598,33 +590,25 @@ public: if (GameObject* go = instance->GetGameObject(_horsemanGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_SAPPHIRON: + case BOSS_SAPPHIRON: if (GameObject* go = instance->GetGameObject(_sapphironGateGUID)) go->SetGoState(GO_STATE_ACTIVE); break; - case EVENT_THADDIUS: + case BOSS_THADDIUS: if (GameObject* go = instance->GetGameObject(_thaddiusPortalGUID)) go->SetPhaseMask(1, true); break; - case EVENT_HORSEMAN: + case BOSS_HORSEMAN: if (GameObject* go = instance->GetGameObject(_horsemanPortalGUID)) go->SetPhaseMask(1, true); break; } } + + return true; } - uint32 GetData(uint32 identifier) const - { - switch(identifier) - { - case EVENT_HORSEMAN: - return Encounters[identifier]; - } - return 0; - } - - void Update(uint32 diff) + void Update(uint32 diff) override { if (_speakTimer) { @@ -666,7 +650,7 @@ public: _horsemanTimer += diff; } - uint64 GetData64(uint32 id) const + uint64 GetData64(uint32 id) const override { switch (id) { @@ -703,21 +687,18 @@ public: return 0; } - std::string GetSaveData() + std::string GetSaveData() override { OUT_SAVE_INST_DATA; std::ostringstream saveStream; - saveStream << "N X X " << Encounters[0] << ' ' << Encounters[1] << ' ' << Encounters[2] << ' ' << Encounters[3] - << ' ' << Encounters[4] << ' ' << Encounters[5] << ' ' << Encounters[6] << ' ' << Encounters[7] - << ' ' << Encounters[8] << ' ' << Encounters[9] << ' ' << Encounters[10] << ' ' << Encounters[11] - << ' ' << Encounters[12] << ' ' << Encounters[13] << ' ' << Encounters[14] << ' ' << immortalAchievement; + saveStream << "N X X " << GetBossSaveData() << ' ' << immortalAchievement; OUT_SAVE_INST_DATA_COMPLETE; return saveStream.str(); } - void Load(const char* in) + void Load(const char* in) override { if (!in) { @@ -735,9 +716,12 @@ public: { for (uint8 i = 0; i < MAX_ENCOUNTERS; ++i) { - loadStream >> Encounters[i]; - if (Encounters[i] == IN_PROGRESS) - Encounters[i] = NOT_STARTED; + uint32 tmpState; + loadStream >> tmpState; + if (tmpState == IN_PROGRESS) + tmpState = NOT_STARTED; + + SetBossState(i, EncounterState(tmpState)); } loadStream >> immortalAchievement; diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h index 10bce8cbb1..25a1bed6d4 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -7,23 +7,23 @@ #include "ScriptPCH.h" -enum NXEncounter +enum Encouters { - EVENT_PATCHWERK = 0, - EVENT_GROBBULUS = 1, - EVENT_GLUTH = 2, - EVENT_NOTH = 3, - EVENT_HEIGAN = 4, - EVENT_LOATHEB = 5, - EVENT_ANUB = 6, - EVENT_FAERLINA = 7, - EVENT_MAEXXNA = 8, - EVENT_THADDIUS = 9, - EVENT_RAZUVIOUS = 10, - EVENT_GOTHIK = 11, - EVENT_HORSEMAN = 12, - EVENT_SAPPHIRON = 13, - EVENT_KELTHUZAD = 14, + BOSS_PATCHWERK = 0, + BOSS_GROBBULUS = 1, + BOSS_GLUTH = 2, + BOSS_NOTH = 3, + BOSS_HEIGAN = 4, + BOSS_LOATHEB = 5, + BOSS_ANUB = 6, + BOSS_FAERLINA = 7, + BOSS_MAEXXNA = 8, + BOSS_THADDIUS = 9, + BOSS_RAZUVIOUS = 10, + BOSS_GOTHIK = 11, + BOSS_HORSEMAN = 12, + BOSS_SAPPHIRON = 13, + BOSS_KELTHUZAD = 14, MAX_ENCOUNTERS, }; diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index a88a56359b..50db4397db 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -1264,6 +1264,108 @@ public: }; +enum BloodsporeRuination +{ + NPC_BLOODMAGE_LAURITH = 25381, + SAY_BLOODMAGE_LAURITH = 0, + EVENT_TALK = 1, + EVENT_RESET_ORIENTATION +}; + +class spell_q11719_bloodspore_ruination_45997 : public SpellScriptLoader +{ +public: + spell_q11719_bloodspore_ruination_45997() : SpellScriptLoader("spell_q11719_bloodspore_ruination_45997") { } + + class spell_q11719_bloodspore_ruination_45997_SpellScript : public SpellScript + { + PrepareSpellScript(spell_q11719_bloodspore_ruination_45997_SpellScript); + + void HandleEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + if (Creature* laurith = caster->FindNearestCreature(NPC_BLOODMAGE_LAURITH, 100.0f)) + laurith->AI()->SetGUID(caster->GetGUID()); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_q11719_bloodspore_ruination_45997_SpellScript::HandleEffect, EFFECT_1, SPELL_EFFECT_SEND_EVENT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_q11719_bloodspore_ruination_45997_SpellScript(); + } +}; + +class npc_bloodmage_laurith : public CreatureScript +{ +public: + npc_bloodmage_laurith() : CreatureScript("npc_bloodmage_laurith") { } + + struct npc_bloodmage_laurithAI : public ScriptedAI + { + npc_bloodmage_laurithAI(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + _events.Reset(); + _playerGUID = 0; + } + + void SetGUID(uint64 guid, int32 /*action*/) override + { + if (_playerGUID) + return; + + _playerGUID = guid; + + if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) + me->SetFacingToObject(player); + + _events.ScheduleEvent(EVENT_TALK, 1000); + } + + void UpdateAI(uint32 diff) override + { + if (UpdateVictim()) + { + DoMeleeAttackIfReady(); + return; + } + + _events.Update(diff); + + if (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_TALK: + if (Player* player = ObjectAccessor::GetPlayer(*me, _playerGUID)) + Talk(SAY_BLOODMAGE_LAURITH, player); + _playerGUID = 0; + _events.ScheduleEvent(EVENT_RESET_ORIENTATION, 5000); + break; + case EVENT_RESET_ORIENTATION: + me->SetFacingTo(me->GetHomePosition().GetOrientation()); + break; + } + } + } + + private: + EventMap _events; + uint64 _playerGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_bloodmage_laurithAI(creature); + } +}; + void AddSC_borean_tundra() { // Ours @@ -1283,4 +1385,6 @@ void AddSC_borean_tundra() new npc_valiance_keep_cannoneer(); new npc_warmage_coldarra(); new npc_hidden_cultist(); + new spell_q11719_bloodspore_ruination_45997(); + new npc_bloodmage_laurith(); } diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp index 367b5231f1..140029352a 100644 --- a/src/server/scripts/Northrend/zone_dalaran.cpp +++ b/src/server/scripts/Northrend/zone_dalaran.cpp @@ -576,13 +576,15 @@ class npc_minigob_manabonk : public CreatureScript events.ScheduleEvent(EVENT_BLINK, 3*IN_MILLISECONDS); break; case EVENT_BLINK: + { DoCast(me, SPELL_IMPROVED_BLINK); Position pos; me->GetRandomNearPosition(pos, (urand(15, 40))); me->GetMotionMaster()->MovePoint(0, pos.m_positionX, pos.m_positionY, pos.m_positionZ); - events.ScheduleEvent(EVENT_DESPAWN, 3*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_DESPAWN, 3 * IN_MILLISECONDS); events.ScheduleEvent(EVENT_DESPAWN_VISUAL, 2.5*IN_MILLISECONDS); break; + } case EVENT_DESPAWN_VISUAL: DoCast(me, SPELL_TELEPORT_VISUAL); break; diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp index 0a9b095316..5297bbc4ce 100644 --- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp +++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_mechano_lord_capacitus.cpp @@ -104,11 +104,13 @@ class boss_mechano_lord_capacitus : public CreatureScript events.ScheduleEvent(EVENT_REFLECTIVE_DAMAGE_SHIELD, 20000); break; case EVENT_SUMMON_NETHER_CHARGE: + { Position pos; me->GetRandomNearPosition(pos, 8.0f); me->SummonCreature(NPC_NETHER_CHARGE, pos, TEMPSUMMON_TIMED_DESPAWN, 18000); events.ScheduleEvent(EVENT_SUMMON_NETHER_CHARGE, 5000); break; + } case EVENT_POSITIVE_SHIFT: me->CastSpell(me, SPELL_POLARITY_SHIFT, true); events.ScheduleEvent(EVENT_POSITIVE_SHIFT, 30000); diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp index a8089857b9..4642e9a131 100644 --- a/src/server/scripts/Pet/pet_mage.cpp +++ b/src/server/scripts/Pet/pet_mage.cpp @@ -52,6 +52,7 @@ class npc_pet_mage_mirror_image : public CreatureScript uint32 selectionTimer; uint64 _ebonGargoyleGUID; + uint32 checktarget; void InitializeAI() { @@ -135,7 +136,7 @@ class npc_pet_mage_mirror_image : public CreatureScript } } - bool MySelectNextTarget() + void MySelectNextTarget() { if (_ebonGargoyleGUID) { @@ -153,21 +154,27 @@ class npc_pet_mage_mirror_image : public CreatureScript // target has cc, search target without cc! if (selection->HasBreakableByDamageCrowdControlAura() || !me->IsValidAttackTarget(selection)) { - return false; + EnterEvadeMode(); + return; } me->getThreatManager().resetAllAggro(); me->AddThreat(selection, 1000000.0f); - AttackStart(selection); - return true; + + if (owner->IsInCombat()) + AttackStart(selection); + } } - return false; + + if (!me->GetVictim() || !me->GetVictim()->IsAlive()) + return; } void Reset() { selectionTimer = 0; + checktarget = 0; } void UpdateAI(uint32 diff) @@ -182,12 +189,15 @@ class npc_pet_mage_mirror_image : public CreatureScript return; } - if (me->GetVictim()->HasBreakableByDamageCrowdControlAura() || !me->GetVictim()->IsAlive()) + checktarget += diff; + if (checktarget >= 1000) { - me->InterruptNonMeleeSpells(false); - if (!MySelectNextTarget()) - EnterEvadeMode(); - return; + if (me->GetVictim()->HasBreakableByDamageCrowdControlAura() || !me->GetVictim()->IsAlive()) + { + MySelectNextTarget(); + me->InterruptNonMeleeSpells(true); // Stop casting if target is C or not Alive. + return; + } } selectionTimer += diff; diff --git a/src/server/scripts/ScriptLoader.cpp b/src/server/scripts/ScriptLoader.cpp index d948a18c55..a5e9f710e9 100644 --- a/src/server/scripts/ScriptLoader.cpp +++ b/src/server/scripts/ScriptLoader.cpp @@ -46,6 +46,7 @@ void AddSC_list_commandscript(); void AddSC_lookup_commandscript(); void AddSC_message_commandscript(); void AddSC_misc_commandscript(); +void AddSC_mmaps_commandscript(); void AddSC_modify_commandscript(); void AddSC_npc_commandscript(); void AddSC_quest_commandscript(); @@ -616,6 +617,7 @@ void AddCommandScripts() AddSC_lookup_commandscript(); AddSC_message_commandscript(); AddSC_misc_commandscript(); + AddSC_mmaps_commandscript(); AddSC_modify_commandscript(); AddSC_npc_commandscript(); AddSC_quest_commandscript(); @@ -1169,3 +1171,15 @@ void AddOutdoorPvPScripts() #endif } + +//~ ********************** Put your custom scripts below, like the commented examples, uncomment and edit ************************************* + + +//~ void AddSC_MySuperScript(); + +void AddCustomScripts() +{ +#ifdef SCRIPTS + //~ AddSC_MySuperScript(); +#endif +} diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index fbbbbbe15a..27263233f8 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -1545,12 +1545,16 @@ class spell_dk_death_grip : public SpellScriptLoader { PrepareSpellScript(spell_dk_death_grip_SpellScript); - SpellCastResult CheckPvPRange() + SpellCastResult CheckCast() { Unit* caster = GetCaster(); - if (Unit* target = GetExplTargetUnit()) - if (target->GetTypeId() == TYPEID_PLAYER && caster->GetExactDist(target) < 8.0f) // xinef: should be 8.0f, but we have to add target size (1.5f) - return SPELL_FAILED_TOO_CLOSE; + Unit* target = GetExplTargetUnit(); + + if (target->GetTypeId() == TYPEID_PLAYER && caster->GetExactDist(target) < 8.0f) // xinef: should be 8.0f, but we have to add target size (1.5f) + return SPELL_FAILED_TOO_CLOSE; + + if (caster->HasUnitState(UNIT_STATE_JUMPING) || caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING)) + return SPELL_FAILED_MOVING; return SPELL_CAST_OK; } @@ -1560,9 +1564,22 @@ class spell_dk_death_grip : public SpellScriptLoader Unit* caster = GetCaster(); Unit* target = GetHitUnit(); Unit* baseTarget = GetExplTargetUnit(); + Creature* targetCreature = GetHitCreature(); if (caster != target) - caster->CastSpell(target, 49560, true); + { + if (targetCreature && (targetCreature->isWorldBoss() || targetCreature->IsDungeonBoss())) + { + return; + } + else + { + caster->CastSpell(target, 49560, true); + const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(1766); // Rogue kick + if (!target->IsImmunedToSpellEffect(spellInfo, EFFECT_0)) + target->InterruptNonMeleeSpells(true); + } + } else baseTarget->CastSpell(caster, 49560, true); } @@ -1592,7 +1609,7 @@ class spell_dk_death_grip : public SpellScriptLoader { if (m_scriptSpellId == 49576) // xinef: base death grip, add pvp range restriction { - OnCheckCast += SpellCheckCastFn(spell_dk_death_grip_SpellScript::CheckPvPRange); + OnCheckCast += SpellCheckCastFn(spell_dk_death_grip_SpellScript::CheckCast); OnEffectHitTarget += SpellEffectFn(spell_dk_death_grip_SpellScript::HandleBaseDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } else diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 4821e57555..f0ac73e39c 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -4431,10 +4431,7 @@ class spell_gen_mount : public SpellScriptLoader if (map == 530 || (map == 571 && target->HasSpell(SPELL_COLD_WEATHER_FLYING))) canFly = true; - float x, y, z; - target->GetPosition(x, y, z); - uint32 areaFlag = target->GetBaseMap()->GetAreaFlag(x, y, z); - AreaTableEntry const* area = sAreaStore.LookupEntry(areaFlag); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(target->GetAreaId()); // Xinef: add battlefield check Battlefield* Bf = sBattlefieldMgr->GetBattlefieldToZoneId(target->GetZoneId()); if (!area || (canFly && ((area->flags & AREA_FLAG_NO_FLY_ZONE) || (Bf && !Bf->CanFlyIn())))) diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 2ea90c230c..45b3ac2640 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -592,7 +592,7 @@ class spell_warr_overpower : public SpellScriptLoader if (Player* target = GetHitPlayer()) if (target->HasUnitState(UNIT_STATE_CASTING)) - target->CastSpell(target, spellId, true); + target->CastSpell(target, spellId, true, 0, 0, GetCaster()->GetGUID()); } void Register() diff --git a/src/server/scripts/World/achievement_scripts.cpp b/src/server/scripts/World/achievement_scripts.cpp index a0a9f893b5..95c4db4801 100644 --- a/src/server/scripts/World/achievement_scripts.cpp +++ b/src/server/scripts/World/achievement_scripts.cpp @@ -112,10 +112,10 @@ class achievement_bg_sa_artillery : public AchievementCriteriaScript } }; -class achievement_arena_kills : public AchievementCriteriaScript +class achievement_arena_by_type : public AchievementCriteriaScript { public: - achievement_arena_kills(char const* name, uint8 arenaType) : AchievementCriteriaScript(name), + achievement_arena_by_type(char const* name, uint8 arenaType) : AchievementCriteriaScript(name), _arenaType(arenaType) { } @@ -129,7 +129,6 @@ class achievement_arena_kills : public AchievementCriteriaScript uint8 const _arenaType; }; - class achievement_sickly_gazelle : public AchievementCriteriaScript { public: @@ -254,9 +253,9 @@ void AddSC_achievement_scripts() new achievement_sickly_gazelle(); new achievement_everything_counts(); new achievement_bg_av_perfection(); - new achievement_arena_kills("achievement_arena_2v2_kills", ARENA_TYPE_2v2); - new achievement_arena_kills("achievement_arena_3v3_kills", ARENA_TYPE_3v3); - new achievement_arena_kills("achievement_arena_5v5_kills", ARENA_TYPE_5v5); + new achievement_arena_by_type("achievement_arena_2v2_check", ARENA_TYPE_2v2); + new achievement_arena_by_type("achievement_arena_3v3_check", ARENA_TYPE_3v3); + new achievement_arena_by_type("achievement_arena_5v5_check", ARENA_TYPE_5v5); new achievement_sa_defense_of_the_ancients(); new achievement_tilted(); new achievement_not_even_a_scratch(); diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt index 58ec995c4e..fdc17078bc 100644 --- a/src/server/worldserver/CMakeLists.txt +++ b/src/server/worldserver/CMakeLists.txt @@ -44,7 +44,7 @@ endif() include_directories( ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/deps/g3dlite/include - ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Detour + ${CMAKE_SOURCE_DIR}/deps/recastnavigation/Detour/Include ${CMAKE_SOURCE_DIR}/deps/gsoap ${CMAKE_SOURCE_DIR}/deps/sockets/include ${CMAKE_SOURCE_DIR}/deps/SFMT diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 5ad7a5bc3b..66fa7fb615 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1434,16 +1434,6 @@ DeletedCharacterTicketTrace = 0 DungeonFinder.OptionsMask = 1 - -# -# DBC.EnforceItemAttributes -# Description: Disallow overriding item attributes stored in DBC files with values from the -# database. -# Default: 1 - (Enabled, Enforce DBC values) -# 0 - (Disabled, Use database values) - -DBC.EnforceItemAttributes = 1 - # # AccountInstancesPerHour # Description: Controls the max amount of different instances player can enter within hour diff --git a/src/tools/map_extractor/CMakeLists.txt b/src/tools/map_extractor/CMakeLists.txt index 2c00a7e130..f127b496a1 100644 --- a/src/tools/map_extractor/CMakeLists.txt +++ b/src/tools/map_extractor/CMakeLists.txt @@ -12,8 +12,10 @@ file(GLOB_RECURSE sources *.cpp *.h) set(include_Dirs + ${CMAKE_SOURCE_DIR}/src/common/Utilities ${CMAKE_SOURCE_DIR}/src/common ${CMAKE_SOURCE_DIR}/deps/libmpq + ${CMAKE_SOURCE_DIR}/modules/worldengine/deps/g3dlite/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/loadlib ) diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index defab209d7..a22abb3ded 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -20,9 +20,12 @@ #include "dbcfile.h" #include "mpq_libmpq04.h" +#include "StringFormat.h" #include "adt.h" #include "wdt.h" + +#include "G3D/Plane.h" #include <fcntl.h> #if defined( __GNUC__ ) @@ -49,11 +52,10 @@ typedef struct } map_id; map_id *map_ids; -uint16 *areas; uint16 *LiqType; -char output_path[128] = "."; -char input_path[128] = "."; -uint32 maxAreaId = 0; +#define MAX_PATH_LENGTH 128 +char output_path[MAX_PATH_LENGTH] = "."; +char input_path[MAX_PATH_LENGTH] = "."; // ************************************************** // Extractor options @@ -244,30 +246,6 @@ uint32 ReadMapDBC() return map_count; } -void ReadAreaTableDBC() -{ - printf("Read AreaTable.dbc file..."); - DBCFile dbc("DBFilesClient\\AreaTable.dbc"); - - if(!dbc.open()) - { - printf("Fatal error: Invalid AreaTable.dbc file format!\n"); - exit(1); - } - - size_t area_count = dbc.getRecordCount(); - size_t maxid = dbc.getMaxId(); - areas = new uint16[maxid + 1]; - memset(areas, 0xff, (maxid + 1) * sizeof(uint16)); - - for(uint32 x = 0; x < area_count; ++x) - areas[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3); - - maxAreaId = dbc.getMaxId(); - - printf("Done! (%u areas loaded)\n", (uint32)area_count); -} - void ReadLiquidTypeTableDBC() { printf("Read LiquidType.dbc file..."); @@ -295,7 +273,7 @@ void ReadLiquidTypeTableDBC() // Map file format data static char const* MAP_MAGIC = "MAPS"; -static char const* MAP_VERSION_MAGIC = "v1.3"; +static char const* MAP_VERSION_MAGIC = "v1.8"; static char const* MAP_AREA_MAGIC = "AREA"; static char const* MAP_HEIGHT_MAGIC = "MHGT"; static char const* MAP_LIQUID_MAGIC = "MLIQ"; @@ -324,9 +302,10 @@ struct map_areaHeader uint16 gridArea; }; -#define MAP_HEIGHT_NO_HEIGHT 0x0001 -#define MAP_HEIGHT_AS_INT16 0x0002 -#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_NO_HEIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_HAS_FLIGHT_BOUNDS 0x0008 struct map_heightHeader { @@ -371,7 +350,7 @@ float selectUInt16StepStore(float maxDiff) return 65535 / maxDiff; } // Temporary grid data store -uint16 area_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]; +uint16 area_ids[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]; float V8[ADT_GRID_SIZE][ADT_GRID_SIZE]; float V9[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]; @@ -385,17 +364,20 @@ uint8 liquid_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID]; bool liquid_show[ADT_GRID_SIZE][ADT_GRID_SIZE]; float liquid_height[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1]; -bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, uint32 build) +int16 flight_box_max[3][3]; +int16 flight_box_min[3][3]; + +bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int /*cell_y*/, int /*cell_x*/, uint32 build) { ADT_file adt; - if (!adt.loadFile(filename)) + if (!adt.loadFile(inputPath)) return false; adt_MCIN *cells = adt.a_grid->getMCIN(); if (!cells) { - printf("Can't find cells in '%s'\n", filename); + printf("Can't find cells in '%s'\n", inputPath.c_str()); return false; } @@ -405,39 +387,25 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, // Prepare map header map_fileheader map; - map.mapMagic = *(uint32 const*)MAP_MAGIC; - map.versionMagic = *(uint32 const*)MAP_VERSION_MAGIC; + map.mapMagic = *reinterpret_cast<uint32 const*>(MAP_MAGIC); + map.versionMagic = *reinterpret_cast<uint32 const*>(MAP_VERSION_MAGIC); map.buildMagic = build; // Get area flags data - 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); - uint32 areaid = cell->areaid; - if(areaid && areaid <= maxAreaId) - { - if(areas[areaid] != 0xffff) - { - area_flags[i][j] = areas[areaid]; - continue; - } - printf("File: %s\nCan't find area flag for areaid %u [%d, %d].\n", filename, areaid, cell->ix, cell->iy); - } - area_flags[i][j] = 0xffff; - } - } + for (int i = 0; i < ADT_CELLS_PER_GRID; i++) + for (int j = 0; j < ADT_CELLS_PER_GRID; j++) + area_ids[i][j] = cells->getMCNK(i, j)->areaid; + //============================================ // Try pack area data //============================================ bool fullAreaData = false; - uint32 areaflag = area_flags[0][0]; - for (int y=0;y<ADT_CELLS_PER_GRID;y++) + uint32 areaId = area_ids[0][0]; + for (int y = 0; y < ADT_CELLS_PER_GRID; ++y) { - for(int x=0;x<ADT_CELLS_PER_GRID;x++) + for (int x = 0; x < ADT_CELLS_PER_GRID; ++x) { - if(area_flags[y][x]!=areaflag) + if (area_ids[y][x] != areaId) { fullAreaData = true; break; @@ -449,27 +417,27 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, map.areaMapSize = sizeof(map_areaHeader); map_areaHeader areaHeader; - areaHeader.fourcc = *(uint32 const*)MAP_AREA_MAGIC; + areaHeader.fourcc = *reinterpret_cast<uint32 const*>(MAP_AREA_MAGIC); areaHeader.flags = 0; if (fullAreaData) { areaHeader.gridArea = 0; - map.areaMapSize+=sizeof(area_flags); + map.areaMapSize += sizeof(area_ids); } else { areaHeader.flags |= MAP_AREA_NO_AREA; - areaHeader.gridArea = (uint16)areaflag; + areaHeader.gridArea = static_cast<uint16>(areaId); } // // Get Height map from grid // - for (int i=0;i<ADT_CELLS_PER_GRID;i++) + for (int i = 0; i<ADT_CELLS_PER_GRID; i++) { - for(int j=0;j<ADT_CELLS_PER_GRID;j++) + for (int j = 0; j<ADT_CELLS_PER_GRID; j++) { - adt_MCNK * cell = cells->getMCNK(i,j); + adt_MCNK * cell = cells->getMCNK(i, j); if (!cell) continue; // Height values for triangles stored in order: @@ -489,22 +457,22 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, // . . . . . . . . // Set map height as grid height - for (int y=0; y <= ADT_CELL_SIZE; y++) + for (int y = 0; y <= ADT_CELL_SIZE; y++) { int cy = i*ADT_CELL_SIZE + y; - for (int x=0; x <= ADT_CELL_SIZE; x++) + for (int x = 0; x <= ADT_CELL_SIZE; x++) { int cx = j*ADT_CELL_SIZE + x; - V9[cy][cx]=cell->ypos; + V9[cy][cx] = cell->ypos; } } - for (int y=0; y < ADT_CELL_SIZE; y++) + for (int y = 0; y < ADT_CELL_SIZE; y++) { int cy = i*ADT_CELL_SIZE + y; - for (int x=0; x < ADT_CELL_SIZE; x++) + for (int x = 0; x < ADT_CELL_SIZE; x++) { int cx = j*ADT_CELL_SIZE + x; - V8[cy][cx]=cell->ypos; + V8[cy][cx] = cell->ypos; } } // Get custom height @@ -512,23 +480,23 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, if (!v) continue; // get V9 height map - for (int y=0; y <= ADT_CELL_SIZE; y++) + for (int y = 0; y <= ADT_CELL_SIZE; y++) { int cy = i*ADT_CELL_SIZE + y; - for (int x=0; x <= ADT_CELL_SIZE; x++) + for (int x = 0; x <= ADT_CELL_SIZE; x++) { int cx = j*ADT_CELL_SIZE + x; - V9[cy][cx]+=v->height_map[y*(ADT_CELL_SIZE*2+1)+x]; + V9[cy][cx] += v->height_map[y*(ADT_CELL_SIZE * 2 + 1) + x]; } } // get V8 height map - for (int y=0; y < ADT_CELL_SIZE; y++) + for (int y = 0; y < ADT_CELL_SIZE; y++) { int cy = i*ADT_CELL_SIZE + y; - for (int x=0; x < ADT_CELL_SIZE; x++) + for (int x = 0; x < ADT_CELL_SIZE; x++) { int cx = j*ADT_CELL_SIZE + x; - V8[cy][cx]+=v->height_map[y*(ADT_CELL_SIZE*2+1)+ADT_CELL_SIZE+1+x]; + V8[cy][cx] += v->height_map[y*(ADT_CELL_SIZE * 2 + 1) + ADT_CELL_SIZE + 1 + x]; } } } @@ -538,18 +506,18 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, //============================================ float maxHeight = -20000; float minHeight = 20000; - for (int y=0; y<ADT_GRID_SIZE; y++) + for (int y = 0; y<ADT_GRID_SIZE; y++) { - for(int x=0;x<ADT_GRID_SIZE;x++) + for (int x = 0; x<ADT_GRID_SIZE; x++) { float h = V8[y][x]; if (maxHeight < h) maxHeight = h; if (minHeight > h) minHeight = h; } } - for (int y=0; y<=ADT_GRID_SIZE; y++) + for (int y = 0; y <= ADT_GRID_SIZE; y++) { - for(int x=0;x<=ADT_GRID_SIZE;x++) + for(int x = 0; x<= ADT_GRID_SIZE; x++) { float h = V9[y][x]; if (maxHeight < h) maxHeight = h; @@ -560,12 +528,12 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, // Check for allow limit minimum height (not store height in deep ochean - allow save some memory) if (CONF_allow_height_limit && minHeight < CONF_use_minHeight) { - for (int y=0; y<ADT_GRID_SIZE; y++) - for(int x=0;x<ADT_GRID_SIZE;x++) + for (int y = 0; y<ADT_GRID_SIZE; y++) + for (int x = 0; x<ADT_GRID_SIZE; x++) if (V8[y][x] < CONF_use_minHeight) V8[y][x] = CONF_use_minHeight; - for (int y=0; y<=ADT_GRID_SIZE; y++) - for(int x=0;x<=ADT_GRID_SIZE;x++) + for (int y = 0; y <= ADT_GRID_SIZE; y++) + for (int x = 0; x <= ADT_GRID_SIZE; x++) if (V9[y][x] < CONF_use_minHeight) V9[y][x] = CONF_use_minHeight; if (minHeight < CONF_use_minHeight) @@ -574,11 +542,19 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, maxHeight = CONF_use_minHeight; } + bool hasFlightBox = false; + if (adt_MFBO* mfbo = adt.a_grid->getMFBO()) + { + memcpy(flight_box_max, &mfbo->max, sizeof(flight_box_max)); + memcpy(flight_box_min, &mfbo->min, sizeof(flight_box_min)); + hasFlightBox = true; + } + map.heightMapOffset = map.areaMapOffset + map.areaMapSize; map.heightMapSize = sizeof(map_heightHeader); map_heightHeader heightHeader; - heightHeader.fourcc = *(uint32 const*)MAP_HEIGHT_MAGIC; + heightHeader.fourcc = *reinterpret_cast<uint32 const*>(MAP_HEIGHT_MAGIC); heightHeader.flags = 0; heightHeader.gridHeight = minHeight; heightHeader.gridMaxHeight = maxHeight; @@ -589,6 +565,12 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, // Not need store if flat surface if (CONF_allow_float_to_int && (maxHeight - minHeight) < CONF_flat_height_delta_limit) heightHeader.flags |= MAP_HEIGHT_NO_HEIGHT; + + if (hasFlightBox) + { + heightHeader.flags |= MAP_HEIGHT_HAS_FLIGHT_BOUNDS; + map.heightMapSize += sizeof(flight_box_max) + sizeof(flight_box_min); + } // Try store as packed in uint16 or uint8 values if (!(heightHeader.flags & MAP_HEIGHT_NO_HEIGHT)) @@ -600,12 +582,12 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, float diff = maxHeight - minHeight; if (diff < CONF_float_to_int8_limit) // As uint8 (max accuracy = CONF_float_to_int8_limit/256) { - heightHeader.flags|=MAP_HEIGHT_AS_INT8; + heightHeader.flags |= MAP_HEIGHT_AS_INT8; step = selectUInt8StepStore(diff); } else if (diff<CONF_float_to_int16_limit) // As uint16 (max accuracy = CONF_float_to_int16_limit/65536) { - heightHeader.flags|=MAP_HEIGHT_AS_INT16; + heightHeader.flags |= MAP_HEIGHT_AS_INT16; step = selectUInt16StepStore(diff); } } @@ -613,32 +595,32 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, // Pack it to int values if need if (heightHeader.flags&MAP_HEIGHT_AS_INT8) { - for (int y=0; y<ADT_GRID_SIZE; y++) - for(int x=0;x<ADT_GRID_SIZE;x++) + for (int y = 0; y<ADT_GRID_SIZE; y++) + for (int x = 0; x<ADT_GRID_SIZE; x++) uint8_V8[y][x] = uint8((V8[y][x] - minHeight) * step + 0.5f); - for (int y=0; y<=ADT_GRID_SIZE; y++) - for(int x=0;x<=ADT_GRID_SIZE;x++) + for (int y = 0; y <= ADT_GRID_SIZE; y++) + for (int x = 0; x <= ADT_GRID_SIZE; x++) uint8_V9[y][x] = uint8((V9[y][x] - minHeight) * step + 0.5f); - map.heightMapSize+= sizeof(uint8_V9) + sizeof(uint8_V8); + map.heightMapSize += sizeof(uint8_V9) + sizeof(uint8_V8); } else if (heightHeader.flags&MAP_HEIGHT_AS_INT16) { - for (int y=0; y<ADT_GRID_SIZE; y++) - for(int x=0;x<ADT_GRID_SIZE;x++) + for (int y = 0; y<ADT_GRID_SIZE; y++) + for (int x = 0; x<ADT_GRID_SIZE; x++) uint16_V8[y][x] = uint16((V8[y][x] - minHeight) * step + 0.5f); - for (int y=0; y<=ADT_GRID_SIZE; y++) - for(int x=0;x<=ADT_GRID_SIZE;x++) + for (int y = 0; y <= ADT_GRID_SIZE; y++) + for (int x = 0; x <= ADT_GRID_SIZE; x++) uint16_V9[y][x] = uint16((V9[y][x] - minHeight) * step + 0.5f); - map.heightMapSize+= sizeof(uint16_V9) + sizeof(uint16_V8); + map.heightMapSize += sizeof(uint16_V9) + sizeof(uint16_V8); } else - map.heightMapSize+= sizeof(V9) + sizeof(V8); + map.heightMapSize += sizeof(V9) + sizeof(V8); } // Get from MCLQ chunk (old) for (int i = 0; i < ADT_CELLS_PER_GRID; i++) { - for(int j = 0; j < ADT_CELLS_PER_GRID; j++) + for (int j = 0; j < ADT_CELLS_PER_GRID; j++) { adt_MCNK *cell = cells->getMCNK(i, j); if (!cell) @@ -658,7 +640,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, if (liquid->flags[y][x] != 0x0F) { liquid_show[cy][cx] = true; - if (liquid->flags[y][x] & (1<<7)) + if (liquid->flags[y][x] & (1 << 7)) liquid_flags[i][j] |= MAP_LIQUID_TYPE_DARK_WATER; ++count; } @@ -666,17 +648,17 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, } uint32 c_flag = cell->flags; - if (c_flag & (1<<2)) + if (c_flag & (1 << 2)) { liquid_entry[i][j] = 1; liquid_flags[i][j] |= MAP_LIQUID_TYPE_WATER; // water } - if (c_flag & (1<<3)) + if (c_flag & (1 << 3)) { liquid_entry[i][j] = 2; liquid_flags[i][j] |= MAP_LIQUID_TYPE_OCEAN; // ocean } - if (c_flag & (1<<4)) + if (c_flag & (1 << 4)) { liquid_entry[i][j] = 3; liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA; // magma/slime @@ -734,7 +716,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, case LIQUID_TYPE_MAGMA: liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA; break; case LIQUID_TYPE_SLIME: liquid_flags[i][j] |= MAP_LIQUID_TYPE_SLIME; break; default: - printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->liquidType, filename, i, j); + printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->liquidType, inputPath.c_str(), i, j); break; } // Dark water detect @@ -877,17 +859,17 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, map.holesSize = 0; // Ok all data prepared - store it - FILE* output = fopen(filename2, "wb"); + FILE* output = fopen(outputPath.c_str(), "wb"); if (!output) { - printf("Can't create the output file '%s'\n", filename2); + printf("Can't create the output file '%s'\n", outputPath.c_str()); return false; } fwrite(&map, sizeof(map), 1, output); // Store area data fwrite(&areaHeader, sizeof(areaHeader), 1, output); if (!(areaHeader.flags&MAP_AREA_NO_AREA)) - fwrite(area_flags, sizeof(area_flags), 1, output); + fwrite(area_ids, sizeof(area_ids), 1, output); // Store height data fwrite(&heightHeader, sizeof(heightHeader), 1, output); @@ -910,6 +892,12 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, } } + if (heightHeader.flags & MAP_HEIGHT_HAS_FLIGHT_BOUNDS) + { + fwrite(flight_box_max, sizeof(flight_box_max), 1, output); + fwrite(flight_box_min, sizeof(flight_box_min), 1, output); + } + // Store liquid data if need if (map.liquidMapOffset) { @@ -937,15 +925,14 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, void ExtractMapsFromMpq(uint32 build) { - char mpq_filename[1024]; - char output_filename[1024]; - char mpq_map_name[1024]; + std::string mpqFileName; + std::string outputFileName; + std::string mpqMapName; printf("Extracting maps...\n"); uint32 map_count = ReadMapDBC(); - ReadAreaTableDBC(); ReadLiquidTypeTableDBC(); std::string path = output_path; @@ -957,9 +944,9 @@ void ExtractMapsFromMpq(uint32 build) { printf("Extract %s (%d/%u) \n", map_ids[z].name, z+1, map_count); // Loadup map grid data - sprintf(mpq_map_name, "World\\Maps\\%s\\%s.wdt", map_ids[z].name, map_ids[z].name); + mpqMapName = Trinity::StringFormat("World\\Maps\\%s\\%s.wdt", map_ids[z].name, map_ids[z].name); WDT_file wdt; - if (!wdt.loadFile(mpq_map_name, false)) + if (!wdt.loadFile(mpqMapName, false)) { // printf("Error loading %s map wdt data\n", map_ids[z].name); continue; @@ -971,17 +958,16 @@ void ExtractMapsFromMpq(uint32 build) { if (!wdt.main->adt_list[y][x].exist) continue; - sprintf(mpq_filename, "World\\Maps\\%s\\%s_%u_%u.adt", map_ids[z].name, map_ids[z].name, x, y); - sprintf(output_filename, "%s/maps/%03u%02u%02u.map", output_path, map_ids[z].id, y, x); - ConvertADT(mpq_filename, output_filename, y, x, build); + mpqFileName = Trinity::StringFormat("World\\Maps\\%s\\%s_%u_%u.adt", map_ids[z].name, map_ids[z].name, x, y); + outputFileName = Trinity::StringFormat("%s/maps/%03u%02u%02u.map", output_path, map_ids[z].id, y, x); + ConvertADT(mpqFileName, outputFileName, y, x, build); } // draw progress bar printf("Processing........................%d%%\r", (100 * (y+1)) / WDT_MAP_SIZE); } } printf("\n"); - delete [] areas; - delete [] map_ids; + delete[] map_ids; } bool ExtractFile( char const* mpq_name, std::string const& filename ) diff --git a/src/tools/map_extractor/adt.cpp b/src/tools/map_extractor/adt.cpp index 574167c216..257d0171aa 100644 --- a/src/tools/map_extractor/adt.cpp +++ b/src/tools/map_extractor/adt.cpp @@ -9,15 +9,16 @@ #include "adt.h" // Helper -int holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888}; -int holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000}; +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'} }; +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' } }; +u_map_fcc MFBOMagic = { { 'O','B','F','M' } }; bool isHole(int holes, int i, int j) { @@ -69,7 +70,7 @@ bool adt_MHDR::prepareLoadedData() if (fcc != MHDRMagic.fcc) return false; - if (size!=sizeof(adt_MHDR)-8) + if (size != sizeof(adt_MHDR) - 8) return false; // Check and prepare MCIN @@ -80,6 +81,9 @@ bool adt_MHDR::prepareLoadedData() if (offsMH2O && !getMH2O()->prepareLoadedData()) return false; + if (offsMFBO && flags & 1 && !getMFBO()->prepareLoadedData()) + return false; + return true; } @@ -142,3 +146,8 @@ bool adt_MCLQ::prepareLoadedData() return true; } + +bool adt_MFBO::prepareLoadedData() +{ + return fcc == MFBOMagic.fcc; +} diff --git a/src/tools/map_extractor/adt.h b/src/tools/map_extractor/adt.h index 71e83cd999..94b830c4ef 100644 --- a/src/tools/map_extractor/adt.h +++ b/src/tools/map_extractor/adt.h @@ -248,6 +248,27 @@ public: }; +// Adt file min/max height chunk +// +class adt_MFBO +{ + union + { + uint32 fcc; + char fcc_txt[4]; + }; +public: + uint32 size; + struct plane + { + int16 coords[9]; + }; + plane max; + plane min; + + bool prepareLoadedData(); +}; + // // Adt file header chunk // @@ -260,12 +281,12 @@ class adt_MHDR public: uint32 size; - uint32 pad; + uint32 flags; uint32 offsMCIN; // MCIN - uint32 offsTex; // MTEX - uint32 offsModels; // MMDX - uint32 offsModelsIds; // MMID - uint32 offsMapObejcts; // MWMO + uint32 offsTex; // MTEX + uint32 offsModels; // MMDX + uint32 offsModelsIds; // MMID + uint32 offsMapObejcts; // MWMO uint32 offsMapObejctsIds; // MWID uint32 offsDoodsDef; // MDDF uint32 offsObjectsDef; // MODF @@ -278,9 +299,22 @@ public: uint32 data5; public: bool prepareLoadedData(); - adt_MCIN *getMCIN(){ return (adt_MCIN *)((uint8 *)&pad+offsMCIN);} - adt_MH2O *getMH2O(){ return offsMH2O ? (adt_MH2O *)((uint8 *)&pad+offsMH2O) : 0;} - + adt_MCIN* getMCIN() + { + return reinterpret_cast<adt_MCIN*>(reinterpret_cast<uint8*>(&flags) + offsMCIN); + } + adt_MH2O* getMH2O() + { + if (offsMH2O) + return reinterpret_cast<adt_MH2O*>(reinterpret_cast<uint8*>(&flags) + offsMH2O); + return nullptr; + } + adt_MFBO* getMFBO() + { + if (flags & 1 && offsMFBO) + return reinterpret_cast<adt_MFBO*>(reinterpret_cast<uint8*>(&flags) + offsMFBO); + return nullptr; + } }; class ADT_file : public FileLoader{ diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp index 2a3b0482f5..1219ef78b5 100644 --- a/src/tools/map_extractor/loadlib.cpp +++ b/src/tools/map_extractor/loadlib.cpp @@ -26,14 +26,14 @@ FileLoader::~FileLoader() free(); } -bool FileLoader::loadFile(char *filename, bool log) +bool FileLoader::loadFile(std::string const& fileName, bool log) { free(); - MPQFile mf(filename); + MPQFile mf(fileName.c_str()); if(mf.isEof()) { if (log) - printf("No such file %s\n", filename); + printf("No such file %s\n", fileName.c_str()); return false; } @@ -45,7 +45,7 @@ bool FileLoader::loadFile(char *filename, bool log) if (prepareLoadedData()) return true; - printf("Error loading %s", filename); + printf("Error loading %s", fileName.c_str()); mf.close(); free(); return false; diff --git a/src/tools/map_extractor/loadlib/loadlib.h b/src/tools/map_extractor/loadlib/loadlib.h index a2169b6b4b..e500f25e6e 100644 --- a/src/tools/map_extractor/loadlib/loadlib.h +++ b/src/tools/map_extractor/loadlib/loadlib.h @@ -7,6 +7,8 @@ #ifndef LOAD_LIB_H #define LOAD_LIB_H +#include <string> + #ifdef _WIN32 typedef __int64 int64; typedef __int32 int32; @@ -65,7 +67,7 @@ public: file_MVER *version; FileLoader(); ~FileLoader(); - bool loadFile(char *filename, bool log = true); + bool loadFile(std::string const& filename, bool log = true); virtual void free(); }; #endif diff --git a/src/tools/mesh_extractor/Utils.h b/src/tools/mesh_extractor/Utils.h index bf98e65f01..2daa23d854 100644 --- a/src/tools/mesh_extractor/Utils.h +++ b/src/tools/mesh_extractor/Utils.h @@ -341,7 +341,7 @@ public: }; #define MMAP_MAGIC 0x4d4d4150 // 'MMAP' -#define MMAP_VERSION 3 +#define MMAP_VERSION 8 struct MmapTileHeader { diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index e5edd4a611..94780d7422 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -17,14 +17,13 @@ #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 +#define MMAP_VERSION 8 struct MmapTileHeader { @@ -32,12 +31,22 @@ struct MmapTileHeader uint32 dtVersion; uint32 mmapVersion; uint32 size; - bool usesLiquids : 1; + char usesLiquids; + char padding[3]; MmapTileHeader() : mmapMagic(MMAP_MAGIC), dtVersion(DT_NAVMESH_VERSION), - mmapVersion(MMAP_VERSION), size(0), usesLiquids(true) {} + mmapVersion(MMAP_VERSION), size(0), usesLiquids(true), padding() {} }; +// All padding fields must be handled and initialized to ensure mmaps_generator will produce binary-identical *.mmtile files +static_assert(sizeof(MmapTileHeader) == 20, "MmapTileHeader size is not correct, adjust the padding field size"); +static_assert(sizeof(MmapTileHeader) == (sizeof(MmapTileHeader::mmapMagic) + + sizeof(MmapTileHeader::dtVersion) + + sizeof(MmapTileHeader::mmapVersion) + + sizeof(MmapTileHeader::size) + + sizeof(MmapTileHeader::usesLiquids) + + sizeof(MmapTileHeader::padding)), "MmapTileHeader has uninitialized padding fields"); + namespace MMAP { MapBuilder::MapBuilder(float maxWalkableAngle, bool skipLiquid, @@ -57,6 +66,10 @@ namespace MMAP m_rcContext = new rcContext(false); + // percentageDone - Initializing + m_totalTiles = 0; + m_totalTilesBuilt = 0; + discoverTiles(); } @@ -65,8 +78,8 @@ namespace MMAP { for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) { - (*it).second->clear(); - delete (*it).second; + (*it).m_tiles->clear(); + delete (*it).m_tiles; } delete m_terrainBuilder; @@ -85,9 +98,9 @@ namespace MMAP 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()) + if (std::find(m_tiles.begin(), m_tiles.end(), mapID) == m_tiles.end()) { - m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>)); + m_tiles.emplace_back(MapTiles(mapID, new std::set<uint32>)); count++; } } @@ -97,8 +110,11 @@ namespace MMAP 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++; + if (std::find(m_tiles.begin(), m_tiles.end(), mapID) == m_tiles.end()) + { + m_tiles.emplace_back(MapTiles(mapID, new std::set<uint32>)); + count++; + } } printf("found %u.\n", count); @@ -106,8 +122,8 @@ namespace MMAP printf("Discovering tiles... "); for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr) { - std::set<uint32>* tiles = (*itr).second; - mapID = (*itr).first; + std::set<uint32>* tiles = (*itr).m_tiles; + mapID = (*itr).m_mapId; sprintf(filter, "%03u*.vmtile", mapID); files.clear(); @@ -136,17 +152,20 @@ namespace MMAP } } printf("found %u.\n\n", count); + + // percentageDone - total tiles to process + m_totalTiles = count; } /**************************************************************************/ std::set<uint32>* MapBuilder::getTileList(uint32 mapID) { - TileList::iterator itr = m_tiles.find(mapID); + TileList::iterator itr = std::find(m_tiles.begin(), m_tiles.end(), mapID); if (itr != m_tiles.end()) - return (*itr).second; + return (*itr).m_tiles; std::set<uint32>* tiles = new std::set<uint32>(); - m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, tiles)); + m_tiles.emplace_back(MapTiles(mapID, tiles)); return tiles; } @@ -157,9 +176,14 @@ namespace MMAP BuilderThreadPool* pool = threads > 0 ? new BuilderThreadPool() : NULL; + m_tiles.sort([](MapTiles a, MapTiles b) + { + return a.m_tiles->size() > b.m_tiles->size(); + }); + for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it) { - uint32 mapID = it->first; + uint32 mapID = it->m_mapId; if (!shouldSkipMap(mapID)) { if (threads > 0) @@ -183,12 +207,14 @@ namespace MMAP } /**************************************************************************/ - void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY) + void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY) const { - maxX = INT_MAX; - maxY = INT_MAX; - minX = INT_MIN; - minY = INT_MIN; + // min and max are initialized to invalid values so the caller iterating the [min, max] range + // will never enter the loop unless valid min/max values are found + maxX = 0; + maxY = 0; + minX = std::numeric_limits<uint32>::max(); + minY = std::numeric_limits<uint32>::max(); float bmin[3] = { 0, 0, 0 }; float bmax[3] = { 0, 0, 0 }; @@ -329,7 +355,7 @@ namespace MMAP void MapBuilder::buildMap(uint32 mapID) { #ifndef __APPLE__ - printf("[Thread %u] Building map %03u:\n", uint32(ACE_Thread::self()), mapID); + //printf("[Thread %u] Building map %03u:\n", uint32(ACE_Thread::self()), mapID); #endif std::set<uint32>* tiles = getTileList(mapID); @@ -382,7 +408,8 @@ namespace MMAP /**************************************************************************/ void MapBuilder::buildTile(uint32 mapID, uint32 tileX, uint32 tileY, dtNavMesh* navMesh) { - printf("[Map %03i] Building tile [%02u,%02u]\n", mapID, tileX, tileY); + // percentageDone - added, now it will show addional reference percentage done of the overall process + printf("%u%% [Map %03i] Building tile [%02u,%02u]\n", percentageDone(m_totalTiles, m_totalTilesBuilt), mapID, tileX, tileY); MeshData meshData; @@ -416,6 +443,9 @@ namespace MMAP // build navmesh tile buildMoveMapTile(mapID, tileX, tileY, meshData, bmin, bmax, navMesh); + + // percentageDone - increment tiles built + m_totalTilesBuilt++; } /**************************************************************************/ @@ -429,7 +459,7 @@ namespace MMAP //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 polyBits = DT_POLY_BITS; int maxTiles = tiles->size(); int maxPolysPerTile = 1 << polyBits; @@ -541,7 +571,9 @@ namespace MMAP 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 + // a value >= 3|6 allows npcs to walk over some fences + // a value >= 4|8 allows npcs to walk over all fences + config.walkableClimb = m_bigBaseUnit ? 4 : 8; config.minRegionArea = rcSqr(60); config.mergeRegionArea = rcSqr(50); config.maxSimplificationError = 1.8f; // eliminates most jagged edges (tiny polygons) @@ -665,7 +697,7 @@ namespace MMAP iv.polyMesh = rcAllocPolyMesh(); if (!iv.polyMesh) { - printf("%s alloc iv.polyMesh FIALED!\n", tileString); + printf("%s alloc iv.polyMesh FAILED!\n", tileString); delete[] pmmerge; delete[] dmmerge; delete[] tiles; @@ -676,7 +708,7 @@ namespace MMAP iv.polyMeshDetail = rcAllocPolyMeshDetail(); if (!iv.polyMeshDetail) { - printf("%s alloc m_dmesh FIALED!\n", tileString); + printf("%s alloc m_dmesh FAILED!\n", tileString); delete[] pmmerge; delete[] dmmerge; delete[] tiles; @@ -741,12 +773,12 @@ namespace MMAP if (params.nvp > DT_VERTS_PER_POLYGON) { printf("%s Invalid verts-per-polygon value! \n", tileString); - continue; + break; } if (params.vertCount >= 0xffff) { printf("%s Too many vertices! \n", tileString); - continue; + break; } if (!params.vertCount || !params.verts) { @@ -754,8 +786,8 @@ namespace MMAP // loaded but those models don't span into this tile // message is an annoyance - //printf("%sNo vertices to build tile! \n", tileString); - continue; + printf("%sNo vertices to build tile! \n", tileString); + break; } if (!params.polyCount || !params.polys || TILES_PER_MAP*TILES_PER_MAP == params.polyCount) @@ -764,19 +796,19 @@ namespace MMAP // 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; + break; } if (!params.detailMeshes || !params.detailVerts || !params.detailTris) { printf("%s No detail mesh to build tile! \n", tileString); - continue; + break; } printf("%s Building navmesh tile...\n", tileString); if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { printf("%s Failed building navmesh tile! \n", tileString); - continue; + break; } dtTileRef tileRef = 0; @@ -787,7 +819,7 @@ namespace MMAP if (!tileRef || dtResult != DT_SUCCESS) { printf("%s Failed adding tile to navmesh! \n", tileString); - continue; + break; } // file output @@ -800,7 +832,7 @@ namespace MMAP sprintf(message, "[Map %03i] Failed to open %s for writing!\n", mapID, fileName); perror(message); navMesh->removeTile(tileRef, NULL, NULL); - continue; + break; } printf("%s Writing to file...\n", tileString); @@ -970,5 +1002,13 @@ namespace MMAP return true; } + + /**************************************************************************/ + uint32 MapBuilder::percentageDone(uint32 totalTiles, uint32 totalTilesBuilt) + { + if (totalTiles) + return totalTilesBuilt * 100 / totalTiles; + return 0; + } } diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h index c99b8da0ea..877102af7e 100644 --- a/src/tools/mmaps_generator/MapBuilder.h +++ b/src/tools/mmaps_generator/MapBuilder.h @@ -9,7 +9,9 @@ #include <vector> #include <set> +#include <atomic> #include <map> +#include <list> #include "TerrainBuilder.h" #include "IntermediateValues.h" @@ -27,7 +29,24 @@ using namespace VMAP; namespace MMAP { - typedef std::map<uint32, std::set<uint32>*> TileList; + struct MapTiles + { + MapTiles() : m_mapId(uint32(-1)), m_tiles(NULL) {} + + MapTiles(uint32 id, std::set<uint32>* tiles) : m_mapId(id), m_tiles(tiles) {} + ~MapTiles() {} + + uint32 m_mapId; + std::set<uint32>* m_tiles; + + bool operator==(uint32 id) + { + return m_mapId == id; + } + }; + + typedef std::list<MapTiles> TileList; + struct Tile { Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {} @@ -49,7 +68,7 @@ namespace MMAP class MapBuilder { public: - MapBuilder(float maxWalkableAngle = 55.f, + MapBuilder(float maxWalkableAngle = 70.f, bool skipLiquid = false, bool skipContinents = false, bool skipJunkMaps = true, @@ -91,11 +110,13 @@ namespace MMAP 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); + void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY) const; bool shouldSkipMap(uint32 mapID); bool isTransportMap(uint32 mapID); bool shouldSkipTile(uint32 mapID, uint32 tileX, uint32 tileY); + // percentageDone - method to calculate percentage + uint32 percentageDone(uint32 totalTiles, uint32 totalTilesDone); TerrainBuilder* m_terrainBuilder; TileList m_tiles; @@ -109,6 +130,9 @@ namespace MMAP float m_maxWalkableAngle; bool m_bigBaseUnit; + // percentageDone - variables to calculate percentage + uint32 m_totalTiles; + std::atomic<uint32> m_totalTilesBuilt; // build performance - not really used for now rcContext* m_rcContext; diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h index 477f974021..e3f23b1a76 100644 --- a/src/tools/mmaps_generator/PathCommon.h +++ b/src/tools/mmaps_generator/PathCommon.h @@ -7,11 +7,9 @@ #ifndef _MMAP_COMMON_H #define _MMAP_COMMON_H +#include "Common.h" #include <string> #include <vector> -#include <ace/OS_NS_sys_time.h> - -#include "Define.h" #ifndef _WIN32 #include <stddef.h> @@ -53,7 +51,7 @@ namespace MMAP if (*++filter == '\0') // wildcard at end of filter means all remaing chars match return true; - while (true) + for (;;) { if (*filter == *str) break; @@ -125,26 +123,6 @@ namespace MMAP 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 index 16201eba1a..1ce6137fc2 100644 --- a/src/tools/mmaps_generator/PathGenerator.cpp +++ b/src/tools/mmaps_generator/PathGenerator.cpp @@ -6,6 +6,7 @@ #include "PathCommon.h" #include "MapBuilder.h" +#include "Timer.h" using namespace MMAP; @@ -230,7 +231,7 @@ int finish(const char* message, int returnValue) int main(int argc, char** argv) { int threads = 3, mapnum = -1; - float maxAngle = 55.0f; + float maxAngle = 70.0f; int tileX = -1, tileY = -1; bool skipLiquid = false, skipContinents = false, @@ -263,7 +264,7 @@ int main(int argc, char** argv) } if (!checkDirectories(debugOutput)) - return silent ? -3 : finish("Press any key to close...", -3); + return silent ? -3 : finish("Press ENTER to close...", -3); MapBuilder builder(maxAngle, skipLiquid, skipContinents, skipJunkMaps, skipBattlegrounds, debugOutput, bigBaseUnit, offMeshInputPath); diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp index 260bb6da7e..3445013bd8 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.cpp +++ b/src/tools/mmaps_generator/TerrainBuilder.cpp @@ -70,7 +70,7 @@ struct map_liquidHeader namespace MMAP { - char const* MAP_VERSION_MAGIC = "v1.3"; + char const* MAP_VERSION_MAGIC = "v1.8"; TerrainBuilder::TerrainBuilder(bool skipLiquid) : m_skipLiquid (skipLiquid){ } TerrainBuilder::~TerrainBuilder() { } @@ -700,7 +700,7 @@ namespace MMAP uint8 type = NAV_EMPTY; // convert liquid type to NavTerrain - switch (liquid->GetType()) + switch (liquid->GetType() & 3) { case 0: case 1: @@ -754,12 +754,12 @@ namespace MMAP } 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 j = 0; j < liqVerts.size(); ++j) + meshData.liquidVerts.append(liqVerts[j].y, liqVerts[j].z, liqVerts[j].x); - for (uint32 i = 0; i < liqTris.size() / 3; ++i) + for (uint32 j = 0; j < liqTris.size() / 3; ++j) { - meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset); + meshData.liquidTris.append(liqTris[j*3+1] + liqOffset, liqTris[j*3+2] + liqOffset, liqTris[j*3] + liqOffset); meshData.liquidType.append(type); } } @@ -894,7 +894,7 @@ namespace MMAP 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, + if (sscanf(buf, "%u %u,%u (%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; diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h index 0bb29fef47..52773320fd 100644 --- a/src/tools/mmaps_generator/TerrainBuilder.h +++ b/src/tools/mmaps_generator/TerrainBuilder.h @@ -69,11 +69,13 @@ namespace MMAP TerrainBuilder(bool skipLiquid); ~TerrainBuilder(); + TerrainBuilder(const TerrainBuilder &tb) = delete; + 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; } + bool usesLiquids() const { return !m_skipLiquid; } // vert and triangle methods static void transform(std::vector<G3D::Vector3> &original, std::vector<G3D::Vector3> &transformed, @@ -109,10 +111,6 @@ namespace MMAP /// 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); }; } diff --git a/src/tools/vmap4_extractor/loadlib/loadlib.h b/src/tools/vmap4_extractor/loadlib/loadlib.h index e55b4a7c56..989f665934 100644 --- a/src/tools/vmap4_extractor/loadlib/loadlib.h +++ b/src/tools/vmap4_extractor/loadlib/loadlib.h @@ -7,6 +7,8 @@ #ifndef LOAD_LIB_H #define LOAD_LIB_H +#include <string> + #ifdef WIN32 typedef __int64 int64; typedef __int32 int32; @@ -59,7 +61,7 @@ public: file_MVER *version; FileLoader(); ~FileLoader(); - bool loadFile(char *filename, bool log = true); + bool loadFile(std::string const& filename, bool log = true); virtual void free(); }; #endif diff --git a/src/tools/vmap4_extractor/model.cpp b/src/tools/vmap4_extractor/model.cpp index 28c1bad31b..07f1f3ec16 100644 --- a/src/tools/vmap4_extractor/model.cpp +++ b/src/tools/vmap4_extractor/model.cpp @@ -84,17 +84,31 @@ bool Model::ConvertToVMAPModel(const char * outfilename) wsize = sizeof(uint32) + sizeof(unsigned short) * nIndexes; fwrite(&wsize, sizeof(int), 1, output); fwrite(&nIndexes, sizeof(uint32), 1, output); - if (nIndexes >0) + if (nIndexes > 0) + { + for (uint32 i = 0; i < nIndexes; ++i) + { + if ((i % 3) - 1 == 0 && i + 1 < nIndexes) + { + uint16 tmp = indices[i]; + indices[i] = indices[i + 1]; + indices[i + 1] = tmp; + } + } fwrite(indices, sizeof(unsigned short), nIndexes, output); - + } fwrite("VERT", 4, 1, output); wsize = sizeof(int) + sizeof(float) * 3 * nVertices; fwrite(&wsize, sizeof(int), 1, output); fwrite(&nVertices, sizeof(int), 1, output); if (nVertices >0) { - for(uint32 vpos=0; vpos <nVertices; ++vpos) - std::swap(vertices[vpos].y, vertices[vpos].z); + for (uint32 vpos = 0; vpos < nVertices; ++vpos) + { + float tmp = vertices[vpos].y; + vertices[vpos].y = -vertices[vpos].z; + vertices[vpos].z = tmp; + } fwrite(vertices, sizeof(float)*3, nVertices, output); } @@ -169,7 +183,6 @@ ModelInstance::ModelInstance(MPQFile& f, char const* ModelInstName, uint32 mapID int realy1 = (int) ((float) pos.z / 533.333333f); int realx2 = (int) ((float) pos.x / 533.333333f); int realy2 = (int) ((float) pos.z / 533.333333f); - fprintf(pDirfile,"%s/%s %f,%f,%f_%f,%f,%f %f %d %d %d,%d %d\n", MapName, ModelInstName, |
