/* * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2 * Copyright (C) 2008-2016 TrinityCore * Copyright (C) 2005-2009 MaNGOS */ #include "Map.h" #include "Battleground.h" #include "CellImpl.h" #include "DynamicTree.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "Object.h" #include "Group.h" #include "InstanceScript.h" #include "MapInstanced.h" #include "MapManager.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Pet.h" #include "ScriptMgr.h" #include "Transport.h" #include "Vehicle.h" #include "VMapFactory.h" #include "LFGMgr.h" #include "Chat.h" union u_map_magic { char asChar[4]; uint32 asUInt; }; u_map_magic MapMagic = { {'M','A','P','S'} }; 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'} }; Map::~Map() { sScriptMgr->OnDestroyMap(this); UnloadAll(); while (!i_worldObjects.empty()) { WorldObject* obj = *i_worldObjects.begin(); ASSERT(obj->IsWorldObject()); //ASSERT(obj->GetTypeId() == TYPEID_CORPSE); obj->RemoveFromWorld(); obj->ResetMap(); } if (!m_scriptSchedule.empty()) sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size()); //MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId()); MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId); } bool Map::ExistMap(uint32 mapid, int gx, int gy) { int len = sWorld->GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; char* tmp = new char[len]; snprintf(tmp, len, (char *)(sWorld->GetDataPath()+"maps/%03u%02u%02u.map").c_str(), mapid, gx, gy); bool ret = false; FILE* pf=fopen(tmp, "rb"); if (!pf) sLog->outError("Map file '%s': does not exist!", tmp); else { map_fileheader header; if (fread(&header, sizeof(header), 1, pf) == 1) { if (header.mapMagic != MapMagic.asUInt || header.versionMagic != MapVersionMagic.asUInt) sLog->outError("Map file '%s' is from an incompatible clientversion. Please recreate using the mapextractor.", tmp); else ret = true; } fclose(pf); } delete [] tmp; return ret; } bool Map::ExistVMap(uint32 mapid, int gx, int gy) { if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager()) { if (vmgr->isMapLoadingEnabled()) { bool exists = vmgr->existsMap((sWorld->GetDataPath()+ "vmaps").c_str(), mapid, gx, gy); if (!exists) { std::string name = vmgr->getDirFileName(mapid, gx, gy); sLog->outError("VMap file '%s' is missing or points to wrong version of vmap file. Redo vmaps with latest version of vmap_assembler.exe.", (sWorld->GetDataPath()+"vmaps/"+name).c_str()); return false; } } } return true; } void Map::LoadMMap(int gx, int gy) { if (!MMAP::MMapFactory::IsPathfindingEnabled(this)) // pussywizard return; int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap(GetId(), gx, gy); switch (mmapLoadResult) { case MMAP::MMAP_LOAD_RESULT_OK: #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("MMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); #endif break; case MMAP::MMAP_LOAD_RESULT_ERROR: #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("Could not load MMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); #endif break; case MMAP::MMAP_LOAD_RESULT_IGNORED: #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug("Ignored MMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); #endif break; } } void Map::LoadVMap(int gx, int gy) { // x and y are swapped !! int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld->GetDataPath()+ "vmaps").c_str(), GetId(), gx, gy); switch (vmapLoadResult) { case VMAP::VMAP_LOAD_RESULT_OK: #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); #endif break; case VMAP::VMAP_LOAD_RESULT_ERROR: #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); #endif break; case VMAP::VMAP_LOAD_RESULT_IGNORED: #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy); #endif break; } } void Map::LoadMap(int gx, int gy, bool reload) { if (i_InstanceId != 0) { if (GridMaps[gx][gy]) return; // load grid map for base map m_parentMap->EnsureGridCreated(GridCoord(63-gx, 63-gy)); GridMaps[gx][gy] = m_parentMap->GridMaps[gx][gy]; return; } if (GridMaps[gx][gy] && !reload) return; //map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?) if (GridMaps[gx][gy]) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("Unloading previously loaded map %u before reloading.", GetId()); #endif sScriptMgr->OnUnloadGridMap(this, GridMaps[gx][gy], gx, gy); delete (GridMaps[gx][gy]); GridMaps[gx][gy]=NULL; } // map file name char *tmp=NULL; int len = sWorld->GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; tmp = new char[len]; snprintf(tmp, len, (char *)(sWorld->GetDataPath()+"maps/%03u%02u%02u.map").c_str(), GetId(), gx, gy); #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("Loading map %s", tmp); #endif // loading data GridMaps[gx][gy] = new GridMap(); if (!GridMaps[gx][gy]->loadData(tmp)) { sLog->outError("Error loading map file: \n %s\n", tmp); } delete [] tmp; sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy); } void Map::LoadMapAndVMap(int gx, int gy) { LoadMap(gx, gy); if (i_InstanceId == 0) { LoadVMap(gx, gy); // Only load the data for the base map LoadMMap(gx, gy); } } Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), _instanceResetPeriod(0), m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()), i_scriptLock(false), _defaultLight(GetDefaultMapLight(id)) { m_parentMap = (_parent ? _parent : this); for (unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx) { for (unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j) { //z code GridMaps[idx][j] =NULL; setNGrid(NULL, idx, j); } } //lets initialize visibility distance for map Map::InitVisibilityDistance(); sScriptMgr->OnCreateMap(this); } void Map::InitVisibilityDistance() { //init visibility for continents m_VisibleDistance = World::GetMaxVisibleDistanceOnContinents(); switch (GetId()) { case 609: // Scarlet Enclave (DK starting zone) m_VisibleDistance = 125.0f; break; case 25: // Scott Test (box map) m_VisibleDistance = 200.0f; break; } } // Template specialization of utility methods template void Map::AddToGrid(T* obj, Cell const& cell) { NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); if (obj->IsWorldObject()) grid->GetGridType(cell.CellX(), cell.CellY()).template AddWorldObject(obj); else grid->GetGridType(cell.CellX(), cell.CellY()).template AddGridObject(obj); } template<> void Map::AddToGrid(Creature* obj, Cell const& cell) { NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); if (obj->IsWorldObject()) grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj); else grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj); obj->SetCurrentCell(cell); } template<> void Map::AddToGrid(GameObject* obj, Cell const& cell) { NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj); obj->SetCurrentCell(cell); } template<> void Map::AddToGrid(DynamicObject* obj, Cell const& cell) { NGridType* grid = getNGrid(cell.GridX(), cell.GridY()); if (obj->IsWorldObject()) grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj); else grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj); obj->SetCurrentCell(cell); } template void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/) { } template<> void Map::SwitchGridContainers(Creature* obj, bool on) { ASSERT(!obj->IsPermanentWorldObject()); CellCoord p = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); if (!p.IsCoordValid()) { sLog->outError("Map::SwitchGridContainers: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); return; } Cell cell(p); if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y))) return; #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug("Switch object " UI64FMTD " from grid[%u, %u] %u", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y, on); #endif NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY()); ASSERT(ngrid != NULL); GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY()); obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add if (on) { grid.AddWorldObject(obj); AddWorldObject(obj); } else { grid.AddGridObject(obj); RemoveWorldObject(obj); } obj->m_isTempWorldObject = on; } template<> void Map::SwitchGridContainers(GameObject* obj, bool on) { ASSERT(!obj->IsPermanentWorldObject()); CellCoord p = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); if (!p.IsCoordValid()) { sLog->outError("Map::SwitchGridContainers: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); return; } Cell cell(p); if (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y))) return; //TC_LOG_DEBUG(LOG_FILTER_MAPS, "Switch object " UI64FMTD " from grid[%u, %u] %u", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y, on); NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY()); ASSERT(ngrid != NULL); GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY()); obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add if (on) { grid.AddWorldObject(obj); AddWorldObject(obj); } else { grid.AddGridObject(obj); RemoveWorldObject(obj); } } template void Map::DeleteFromWorld(T* obj) { // Note: In case resurrectable corpse and pet its removed from global lists in own destructor delete obj; } template<> void Map::DeleteFromWorld(Player* player) { sObjectAccessor->RemoveObject(player); // pussywizard: optimization std::string charName = player->GetName(); std::transform(charName.begin(), charName.end(), charName.begin(), ::tolower); sObjectAccessor->playerNameToPlayerPointer.erase(charName); sObjectAccessor->RemoveUpdateObject(player); //TODO: I do not know why we need this, it should be removed in ~Object anyway delete player; } void Map::EnsureGridCreated(const GridCoord &p) { if (getNGrid(p.x_coord, p.y_coord)) // pussywizard return; TRINITY_GUARD(ACE_Thread_Mutex, GridLock); EnsureGridCreated_i(p); } //Create NGrid so the object can be added to it //But object data is not loaded here void Map::EnsureGridCreated_i(const GridCoord &p) { if (!getNGrid(p.x_coord, p.y_coord)) { // pussywizard: moved setNGrid to the end of the function NGridType* ngt = new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord); // build a linkage between this map and NGridType buildNGridLinkage(ngt); // pussywizard: getNGrid(x, y) changed to: ngt //z coord int gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord; int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord; if (!GridMaps[gx][gy]) { LoadMapAndVMap(gx, gy); } // pussywizard: moved here setNGrid(ngt, p.x_coord, p.y_coord); } } //Create NGrid and load the object data in it bool Map::EnsureGridLoaded(const Cell &cell) { EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY())); NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); ASSERT(grid != NULL); if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY())) { //if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY())) //{ #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_MAPS, "Loading grid[%u, %u] for map %u instance %u", cell.GridX(), cell.GridY(), GetId(), i_InstanceId); #endif setGridObjectDataLoaded(true, cell.GridX(), cell.GridY()); ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); // Add resurrectable corpses to world object list in grid sObjectAccessor->AddCorpsesToGrid(GridCoord(cell.GridX(), cell.GridY()), grid->GetGridType(cell.CellX(), cell.CellY()), this); Balance(); return true; //} } return false; } void Map::LoadGrid(float x, float y) { EnsureGridLoaded(Cell(x, y)); } bool Map::AddPlayerToMap(Player* player) { CellCoord cellCoord = Trinity::ComputeCellCoord(player->GetPositionX(), player->GetPositionY()); if (!cellCoord.IsCoordValid()) { sLog->outError("Map::Add: Player (GUID: %u) has invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord); return false; } Cell cell(cellCoord); EnsureGridLoaded(cell); AddToGrid(player, cell); // Check if we are adding to correct map ASSERT (player->GetMap() == this); player->SetMap(this); player->AddToWorld(); SendInitTransports(player); SendInitSelf(player); SendZoneDynamicInfo(player); player->m_clientGUIDs.clear(); player->UpdateObjectVisibility(false); sScriptMgr->OnPlayerEnterMap(this, player); return true; } template void Map::InitializeObject(T* /*obj*/) { } template<> void Map::InitializeObject(Creature* /*obj*/) { //obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; // pussywizard: this is shit } template<> void Map::InitializeObject(GameObject* /*obj*/) { //obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; // pussywizard: this is shit } template bool Map::AddToMap(T* obj, bool checkTransport) { //TODO: Needs clean up. An object should not be added to map twice. if (obj->IsInWorld()) { ASSERT(obj->IsInGrid()); obj->UpdateObjectVisibility(true); return true; } CellCoord cellCoord = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); //It will create many problems (including crashes) if an object is not added to grid after creation //The correct way to fix it is to make AddToMap return false and delete the object if it is not added to grid //But now AddToMap is used in too many places, I will just see how many ASSERT failures it will cause ASSERT(cellCoord.IsCoordValid()); if (!cellCoord.IsCoordValid()) { sLog->outError("Map::Add: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord); return false; //Should delete object } Cell cell(cellCoord); if (obj->isActiveObject()) EnsureGridLoaded(cell); else EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY())); AddToGrid(obj, cell); //Must already be set before AddToMap. Usually during obj->Create. //obj->SetMap(this); obj->AddToWorld(); if (checkTransport) if (!(obj->GetTypeId() == TYPEID_GAMEOBJECT && obj->ToGameObject()->IsTransport())) // dont add transport to transport ;d if (Transport* transport = GetTransportForPos(obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj)) transport->AddPassenger(obj, true); InitializeObject(obj); if (obj->isActiveObject()) AddToActive(obj); //something, such as vehicle, needs to be update immediately //also, trigger needs to cast spell, if not update, cannot see visual obj->UpdateObjectVisibility(true); // Xinef: little hack for vehicles, accessories have to be added after visibility update so they wont fall off the vehicle, moved from Creature::AIM_Initialize // Initialize vehicle, this is done only for summoned npcs, DB creatures are handled by grid loaders if (obj->GetTypeId() == TYPEID_UNIT) if (Vehicle* vehicle = obj->ToCreature()->GetVehicleKit()) vehicle->Reset(); return true; } template<> bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/) { //TODO: Needs clean up. An object should not be added to map twice. if (obj->IsInWorld()) return true; CellCoord cellCoord = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); if (!cellCoord.IsCoordValid()) { sLog->outError("Map::Add: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord); return false; //Should delete object } Cell cell(cellCoord); if (obj->isActiveObject()) EnsureGridLoaded(cell); obj->AddToWorld(); if (obj->isActiveObject()) AddToActive(obj); _transports.insert(obj); // Broadcast creation to players if (!GetPlayers().isEmpty()) { for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { if (itr->GetSource()->GetTransport() != obj) { UpdateData data; obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); WorldPacket packet; data.BuildPacket(&packet); itr->GetSource()->SendDirectMessage(&packet); } } } return true; } bool Map::IsGridLoaded(const GridCoord &p) const { return (getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord)); } void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor &gridVisitor, TypeContainerVisitor &worldVisitor) { // Check for valid position if (!obj->IsPositionValid()) return; if (obj->GetGridActivationRange() <= 0.0f) // pussywizard: gameobjects for example are on active lists, but range is equal to 0 (they just prevent grid unloading) return; // Update mobs/objects in ALL visible cells around object! CellArea area = Cell::CalculateCellArea(obj->GetPositionX(), obj->GetPositionY(), obj->GetGridActivationRange()); for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x) { for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y) { // marked cells are those that have been visited // don't visit the same cell twice uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; if (isCellMarked(cell_id)) continue; markCell(cell_id); CellCoord pair(x, y); Cell cell(pair); //cell.SetNoCreate(); // in mmaps this is missing Visit(cell, gridVisitor); Visit(cell, worldVisitor); } } } void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) { uint32 mapId = GetId(); // pussywizard: for crashlogs sLog->outDebug(LOG_FILTER_POOLSYS, "%u", mapId); // pussywizard: for crashlogs if (t_diff) _dynamicTree.update(t_diff); /// update worldsessions for existing players for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter) { Player* player = m_mapRefIter->GetSource(); if (player && player->IsInWorld()) { //player->Update(t_diff); WorldSession* session = player->GetSession(); MapSessionFilter updater(session); session->Update(s_diff, updater); } } if (!t_diff) { for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter) { Player* player = m_mapRefIter->GetSource(); if (!player || !player->IsInWorld()) continue; // update players at tick player->Update(s_diff); } HandleDelayedVisibility(); return; } /// update active cells around players and active objects resetMarkedCells(); Trinity::ObjectUpdater updater(t_diff); // for creature TypeContainerVisitor grid_object_update(updater); // for pets TypeContainerVisitor world_object_update(updater); // pussywizard: container for far creatures in combat with players std::vector updateList; updateList.reserve(10); // the player iterator is stored in the map object // to make sure calls to Map::Remove don't invalidate it for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter) { Player* player = m_mapRefIter->GetSource(); if (!player || !player->IsInWorld()) continue; // update players at tick player->Update(s_diff); VisitNearbyCellsOf(player, grid_object_update, world_object_update); // handle updates for creatures in combat with player and are more than X yards away if (player->IsInCombat()) { updateList.clear(); float rangeSq = player->GetGridActivationRange() - 1.0f; rangeSq = rangeSq*rangeSq; HostileReference* ref = player->getHostileRefManager().getFirst(); while (ref) { if (Unit* unit = ref->GetSource()->GetOwner()) if (Creature* cre = unit->ToCreature()) if (cre->FindMap() == player->FindMap() && cre->GetExactDist2dSq(player) > rangeSq) updateList.push_back(cre); ref = ref->next(); } for (std::vector::const_iterator itr = updateList.begin(); itr != updateList.end(); ++itr) VisitNearbyCellsOf(*itr, grid_object_update, world_object_update); } } // non-player active objects, increasing iterator in the loop in case of object removal for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end();) { WorldObject* obj = *m_activeNonPlayersIter; ++m_activeNonPlayersIter; if (!obj || !obj->IsInWorld()) continue; VisitNearbyCellsOf(obj, grid_object_update, world_object_update); } for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) // pussywizard: transports updated after VisitNearbyCellsOf, grids around are loaded, everything ok { MotionTransport* transport = *_transportsUpdateIter; ++_transportsUpdateIter; if (!transport->IsInWorld()) continue; transport->Update(t_diff); } ///- Process necessary scripts if (!m_scriptSchedule.empty()) { i_scriptLock = true; ScriptsProcess(); i_scriptLock = false; } MoveAllCreaturesInMoveList(); MoveAllGameObjectsInMoveList(); MoveAllDynamicObjectsInMoveList(); HandleDelayedVisibility(); sScriptMgr->OnMapUpdate(this, t_diff); BuildAndSendUpdateForObjects(); // pussywizard sLog->outDebug(LOG_FILTER_POOLSYS, "%u", mapId); // pussywizard: for crashlogs } void Map::HandleDelayedVisibility() { if (i_objectsForDelayedVisibility.empty()) return; for (UNORDERED_SET::iterator itr = i_objectsForDelayedVisibility.begin(); itr != i_objectsForDelayedVisibility.end(); ++itr) (*itr)->ExecuteDelayedUnitRelocationEvent(); i_objectsForDelayedVisibility.clear(); } struct ResetNotifier { templateinline void resetNotify(GridRefManager &m) { for (typename GridRefManager::iterator iter=m.begin(); iter != m.end(); ++iter) iter->GetSource()->ResetAllNotifies(); } template void Visit(GridRefManager &) {} void Visit(CreatureMapType &m) { resetNotify(m);} void Visit(PlayerMapType &m) { resetNotify(m);} }; void Map::RemovePlayerFromMap(Player* player, bool remove) { player->getHostileRefManager().deleteReferences(); // pussywizard: multithreading crashfix bool inWorld = player->IsInWorld(); player->RemoveFromWorld(); SendRemoveTransports(player); if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers() player->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility(true) if (player->IsInGrid()) player->RemoveFromGrid(); else ASSERT(remove); //maybe deleted in logoutplayer when player is not in a map if (remove) { sScriptMgr->OnPlayerLeaveMap(this, player); DeleteFromWorld(player); } } void Map::AfterPlayerUnlinkFromMap() { } template void Map::RemoveFromMap(T *obj, bool remove) { bool inWorld = obj->IsInWorld() && obj->GetTypeId() >= TYPEID_UNIT && obj->GetTypeId() <= TYPEID_GAMEOBJECT; obj->RemoveFromWorld(); if (obj->isActiveObject()) RemoveFromActive(obj); if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers() obj->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility() obj->RemoveFromGrid(); obj->ResetMap(); if (remove) DeleteFromWorld(obj); } template<> void Map::RemoveFromMap(MotionTransport* obj, bool remove) { obj->RemoveFromWorld(); if (obj->isActiveObject()) RemoveFromActive(obj); Map::PlayerList const& players = GetPlayers(); if (!players.isEmpty()) { UpdateData data; obj->BuildOutOfRangeUpdateBlock(&data); WorldPacket packet; data.BuildPacket(&packet); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (itr->GetSource()->GetTransport() != obj) itr->GetSource()->SendDirectMessage(&packet); } if (_transportsUpdateIter != _transports.end()) { TransportsContainer::iterator itr = _transports.find(obj); if (itr == _transports.end()) return; if (itr == _transportsUpdateIter) ++_transportsUpdateIter; _transports.erase(itr); } else _transports.erase(obj); obj->ResetMap(); if (remove) { // if option set then object already saved at this moment if (!sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY)) obj->SaveRespawnTime(); DeleteFromWorld(obj); } } void Map::PlayerRelocation(Player* player, float x, float y, float z, float o) { Cell old_cell(player->GetPositionX(), player->GetPositionY()); Cell new_cell(x, y); if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell)) { player->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddToGrid(player, new_cell); } player->Relocate(x, y, z, o); if (player->IsVehicle()) player->GetVehicleKit()->RelocatePassengers(); player->UpdateObjectVisibility(false); } void Map::CreatureRelocation(Creature* creature, float x, float y, float z, float o) { Cell old_cell = creature->GetCurrentCell(); Cell new_cell(x, y); if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell)) { if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddCreatureToMoveList(creature); } else RemoveCreatureFromMoveList(creature); creature->Relocate(x, y, z, o); if (creature->IsVehicle()) creature->GetVehicleKit()->RelocatePassengers(); creature->UpdateObjectVisibility(false); } void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float o) { Cell old_cell = go->GetCurrentCell(); Cell new_cell(x, y); if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell)) { if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddGameObjectToMoveList(go); } else RemoveGameObjectFromMoveList(go); go->Relocate(x, y, z, o); go->UpdateModelPosition(); go->UpdateObjectVisibility(false); } void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float z, float o) { Cell old_cell = dynObj->GetCurrentCell(); Cell new_cell(x, y); if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell)) { if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddDynamicObjectToMoveList(dynObj); } else RemoveDynamicObjectFromMoveList(dynObj); dynObj->Relocate(x, y, z, o); dynObj->UpdateObjectVisibility(false); } void Map::AddCreatureToMoveList(Creature* c) { if (c->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _creaturesToMove.push_back(c); c->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; } void Map::RemoveCreatureFromMoveList(Creature* c) { if (c->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) c->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } void Map::AddGameObjectToMoveList(GameObject* go) { if (go->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _gameObjectsToMove.push_back(go); go->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; } void Map::RemoveGameObjectFromMoveList(GameObject* go) { if (go->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) go->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj) { if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_NONE) _dynamicObjectsToMove.push_back(dynObj); dynObj->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE; } void Map::RemoveDynamicObjectFromMoveList(DynamicObject* dynObj) { if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE) dynObj->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE; } void Map::MoveAllCreaturesInMoveList() { for (std::vector::iterator itr = _creaturesToMove.begin(); itr != _creaturesToMove.end(); ++itr) { Creature* c = *itr; if (c->FindMap() != this) continue; if (c->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) { c->_moveState = MAP_OBJECT_CELL_MOVE_NONE; continue; } c->_moveState = MAP_OBJECT_CELL_MOVE_NONE; if (!c->IsInWorld()) continue; Cell const& old_cell = c->GetCurrentCell(); Cell new_cell(c->GetPositionX(), c->GetPositionY()); c->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddToGrid(c, new_cell); } _creaturesToMove.clear(); } void Map::MoveAllGameObjectsInMoveList() { for (std::vector::iterator itr = _gameObjectsToMove.begin(); itr != _gameObjectsToMove.end(); ++itr) { GameObject* go = *itr; if (go->FindMap() != this) continue; if (go->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) { go->_moveState = MAP_OBJECT_CELL_MOVE_NONE; continue; } go->_moveState = MAP_OBJECT_CELL_MOVE_NONE; if (!go->IsInWorld()) continue; Cell const& old_cell = go->GetCurrentCell(); Cell new_cell(go->GetPositionX(), go->GetPositionY()); go->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddToGrid(go, new_cell); } _gameObjectsToMove.clear(); } void Map::MoveAllDynamicObjectsInMoveList() { for (std::vector::iterator itr = _dynamicObjectsToMove.begin(); itr != _dynamicObjectsToMove.end(); ++itr) { DynamicObject* dynObj = *itr; if (dynObj->FindMap() != this) continue; if (dynObj->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE) { dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; continue; } dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; if (!dynObj->IsInWorld()) continue; Cell const& old_cell = dynObj->GetCurrentCell(); Cell new_cell(dynObj->GetPositionX(), dynObj->GetPositionY()); dynObj->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) EnsureGridLoaded(new_cell); AddToGrid(dynObj, new_cell); } _dynamicObjectsToMove.clear(); } bool Map::UnloadGrid(NGridType& ngrid) { // pussywizard: UnloadGrid only done when whole map is unloaded, no need to worry about moving npcs between grids, etc. const uint32 x = ngrid.getX(); const uint32 y = ngrid.getY(); { ObjectGridCleaner worker; TypeContainerVisitor visitor(worker); ngrid.VisitAllGrids(visitor); } RemoveAllObjectsInRemoveList(); { ObjectGridUnloader worker; TypeContainerVisitor visitor(worker); ngrid.VisitAllGrids(visitor); } ASSERT(i_objectsToRemove.empty()); delete &ngrid; setNGrid(NULL, x, y); int gx = (MAX_NUMBER_OF_GRIDS - 1) - x; int gy = (MAX_NUMBER_OF_GRIDS - 1) - y; if (i_InstanceId == 0) { if (GridMaps[gx][gy]) { GridMaps[gx][gy]->unloadData(); delete GridMaps[gx][gy]; } // x and y are swapped VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy); MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy); } GridMaps[gx][gy] = NULL; #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug("Unloading grid[%u, %u] for map %u finished", x, y, GetId()); #endif return true; } void Map::RemoveAllPlayers() { if (HavePlayers()) { for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) { Player* player = itr->GetSource(); if (!player->IsBeingTeleportedFar()) { // this is happening for bg sLog->outError("Map::UnloadAll: player %s is still in map %u during unload, this should not happen!", player->GetName().c_str(), GetId()); player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation()); } } } } void Map::UnloadAll() { // clear all delayed moves, useless anyway do this moves before map unload. _creaturesToMove.clear(); _gameObjectsToMove.clear(); for (GridRefManager::iterator i = GridRefManager::begin(); i != GridRefManager::end();) { NGridType &grid(*i->GetSource()); ++i; UnloadGrid(grid); // deletes the grid and removes it from the GridRefManager } // pussywizard: crashfix, some npc can be left on transport (not a default passenger) if (!AllTransportsEmpty()) AllTransportsRemovePassengers(); for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();) { MotionTransport* transport = *itr; ++itr; transport->RemoveFromWorld(); delete transport; } _transports.clear(); } // ***************************** // Grid function // ***************************** GridMap::GridMap() { _flags = 0; // Area data _gridArea = 0; _areaMap = nullptr; // Height level data _gridHeight = INVALID_HEIGHT; _gridGetHeight = &GridMap::getHeightFromFlat; _gridIntHeightMultiplier = 0; m_V9 = nullptr; m_V8 = nullptr; _maxHeight = nullptr; _minHeight = nullptr; // Liquid data _liquidType = 0; _liquidOffX = 0; _liquidOffY = 0; _liquidWidth = 0; _liquidHeight = 0; _liquidLevel = INVALID_HEIGHT; _liquidEntry = nullptr; _liquidFlags = nullptr; _liquidMap = nullptr; } GridMap::~GridMap() { unloadData(); } bool GridMap::loadData(char *filename) { // Unload old data if exist unloadData(); map_fileheader header; // Not return error if file not found FILE* in = fopen(filename, "rb"); if (!in) return true; if (fread(&header, sizeof(header), 1, in) != 1) { fclose(in); return false; } if (header.mapMagic == MapMagic.asUInt && header.versionMagic == MapVersionMagic.asUInt) { // loadup area data if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize)) { sLog->outError("Error loading map area data\n"); fclose(in); return false; } // loadup height data if (header.heightMapOffset && !loadHeightData(in, header.heightMapOffset, header.heightMapSize)) { sLog->outError("Error loading map height data\n"); fclose(in); return false; } // loadup liquid data if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize)) { sLog->outError("Error loading map liquids data\n"); fclose(in); return false; } fclose(in); return true; } sLog->outError("Map file '%s' is from an incompatible clientversion. Please recreate using the mapextractor.", filename); fclose(in); return false; } void GridMap::unloadData() { delete[] _areaMap; delete[] m_V9; delete[] m_V8; delete[] _maxHeight; delete[] _minHeight; delete[] _liquidEntry; delete[] _liquidFlags; delete[] _liquidMap; _areaMap = nullptr; m_V9 = nullptr; m_V8 = nullptr; _maxHeight = nullptr; _minHeight = nullptr; _liquidEntry = nullptr; _liquidFlags = nullptr; _liquidMap = nullptr; _gridGetHeight = &GridMap::getHeightFromFlat; } bool GridMap::loadAreaData(FILE* in, uint32 offset, uint32 /*size*/) { map_areaHeader header; fseek(in, offset, SEEK_SET); if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapAreaMagic.asUInt) return false; _gridArea = header.gridArea; if (!(header.flags & MAP_AREA_NO_AREA)) { _areaMap = new uint16 [16*16]; if (fread(_areaMap, sizeof(uint16), 16*16, in) != 16*16) return false; } return true; } bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/) { map_heightHeader header; fseek(in, offset, SEEK_SET); if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapHeightMagic.asUInt) return false; _gridHeight = header.gridHeight; if (!(header.flags & MAP_HEIGHT_NO_HEIGHT)) { if ((header.flags & MAP_HEIGHT_AS_INT16)) { m_uint16_V9 = new uint16 [129*129]; m_uint16_V8 = new uint16 [128*128]; if (fread(m_uint16_V9, sizeof(uint16), 129*129, in) != 129*129 || fread(m_uint16_V8, sizeof(uint16), 128*128, in) != 128*128) return false; _gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535; _gridGetHeight = &GridMap::getHeightFromUint16; } else if ((header.flags & MAP_HEIGHT_AS_INT8)) { m_uint8_V9 = new uint8 [129*129]; m_uint8_V8 = new uint8 [128*128]; if (fread(m_uint8_V9, sizeof(uint8), 129*129, in) != 129*129 || fread(m_uint8_V8, sizeof(uint8), 128*128, in) != 128*128) return false; _gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 255; _gridGetHeight = &GridMap::getHeightFromUint8; } else { m_V9 = new float [129*129]; m_V8 = new float [128*128]; if (fread(m_V9, sizeof(float), 129*129, in) != 129*129 || fread(m_V8, sizeof(float), 128*128, in) != 128*128) return false; _gridGetHeight = &GridMap::getHeightFromFloat; } } 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; } bool GridMap::loadLiquidData(FILE* in, uint32 offset, uint32 /*size*/) { map_liquidHeader header; fseek(in, offset, SEEK_SET); if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapLiquidMagic.asUInt) return false; _liquidType = header.liquidType; _liquidOffX = header.offsetX; _liquidOffY = header.offsetY; _liquidWidth = header.width; _liquidHeight = header.height; _liquidLevel = header.liquidLevel; if (!(header.flags & MAP_LIQUID_NO_TYPE)) { _liquidEntry = new uint16[16*16]; if (fread(_liquidEntry, sizeof(uint16), 16*16, in) != 16*16) return false; _liquidFlags = new uint8[16*16]; if (fread(_liquidFlags, sizeof(uint8), 16*16, in) != 16*16) return false; } if (!(header.flags & MAP_LIQUID_NO_HEIGHT)) { _liquidMap = new float[uint32(_liquidWidth) * uint32(_liquidHeight)]; if (fread(_liquidMap, sizeof(float), _liquidWidth*_liquidHeight, in) != (uint32(_liquidWidth) * uint32(_liquidHeight))) return false; } return true; } uint16 GridMap::getArea(float x, float y) const { if (!_areaMap) return _gridArea; x = 16 * (32 - x/SIZE_OF_GRIDS); y = 16 * (32 - y/SIZE_OF_GRIDS); int lx = (int)x & 15; int ly = (int)y & 15; return _areaMap[lx*16 + ly]; } float GridMap::getHeightFromFlat(float /*x*/, float /*y*/) const { return _gridHeight; } float GridMap::getHeightFromFloat(float x, float y) const { if (!m_V8 || !m_V9) return _gridHeight; x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); int x_int = (int)x; int y_int = (int)y; x -= x_int; y -= y_int; x_int&=(MAP_RESOLUTION - 1); y_int&=(MAP_RESOLUTION - 1); // Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid // +--------------> X // | h1-------h2 Coordinates is: // | | \ 1 / | h1 0, 0 // | | \ / | h2 0, 1 // | | 2 h5 3 | h3 1, 0 // | | / \ | h4 1, 1 // | | / 4 \ | h5 1/2, 1/2 // | h3-------h4 // V Y // For find height need // 1 - detect triangle // 2 - solve linear equation from triangle points // Calculate coefficients for solve h = a*x + b*y + c float a, b, c; // Select triangle: if (x+y < 1) { if (x > y) { // 1 triangle (h1, h2, h5 points) float h1 = m_V9[(x_int)*129 + y_int]; float h2 = m_V9[(x_int+1)*129 + y_int]; float h5 = 2 * m_V8[x_int*128 + y_int]; a = h2-h1; b = h5-h1-h2; c = h1; } else { // 2 triangle (h1, h3, h5 points) float h1 = m_V9[x_int*129 + y_int ]; float h3 = m_V9[x_int*129 + y_int+1]; float h5 = 2 * m_V8[x_int*128 + y_int]; a = h5 - h1 - h3; b = h3 - h1; c = h1; } } else { if (x > y) { // 3 triangle (h2, h4, h5 points) float h2 = m_V9[(x_int+1)*129 + y_int ]; float h4 = m_V9[(x_int+1)*129 + y_int+1]; float h5 = 2 * m_V8[x_int*128 + y_int]; a = h2 + h4 - h5; b = h4 - h2; c = h5 - h4; } else { // 4 triangle (h3, h4, h5 points) float h3 = m_V9[(x_int)*129 + y_int+1]; float h4 = m_V9[(x_int+1)*129 + y_int+1]; float h5 = 2 * m_V8[x_int*128 + y_int]; a = h4 - h3; b = h3 + h4 - h5; c = h5 - h4; } } // Calculate height return a * x + b * y + c; } float GridMap::getHeightFromUint8(float x, float y) const { if (!m_uint8_V8 || !m_uint8_V9) return _gridHeight; x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); int x_int = (int)x; int y_int = (int)y; x -= x_int; y -= y_int; x_int&=(MAP_RESOLUTION - 1); y_int&=(MAP_RESOLUTION - 1); int32 a, b, c; uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int]; if (x+y < 1) { if (x > y) { // 1 triangle (h1, h2, h5 points) int32 h1 = V9_h1_ptr[ 0]; int32 h2 = V9_h1_ptr[129]; int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; a = h2-h1; b = h5-h1-h2; c = h1; } else { // 2 triangle (h1, h3, h5 points) int32 h1 = V9_h1_ptr[0]; int32 h3 = V9_h1_ptr[1]; int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; a = h5 - h1 - h3; b = h3 - h1; c = h1; } } else { if (x > y) { // 3 triangle (h2, h4, h5 points) int32 h2 = V9_h1_ptr[129]; int32 h4 = V9_h1_ptr[130]; int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; a = h2 + h4 - h5; b = h4 - h2; c = h5 - h4; } else { // 4 triangle (h3, h4, h5 points) int32 h3 = V9_h1_ptr[ 1]; int32 h4 = V9_h1_ptr[130]; int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int]; a = h4 - h3; b = h3 + h4 - h5; c = h5 - h4; } } // Calculate height return (float)((a * x) + (b * y) + c)*_gridIntHeightMultiplier + _gridHeight; } float GridMap::getHeightFromUint16(float x, float y) const { if (!m_uint16_V8 || !m_uint16_V9) return _gridHeight; x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); int x_int = (int)x; int y_int = (int)y; x -= x_int; y -= y_int; x_int&=(MAP_RESOLUTION - 1); y_int&=(MAP_RESOLUTION - 1); int32 a, b, c; uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int]; if (x+y < 1) { if (x > y) { // 1 triangle (h1, h2, h5 points) int32 h1 = V9_h1_ptr[ 0]; int32 h2 = V9_h1_ptr[129]; int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; a = h2-h1; b = h5-h1-h2; c = h1; } else { // 2 triangle (h1, h3, h5 points) int32 h1 = V9_h1_ptr[0]; int32 h3 = V9_h1_ptr[1]; int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; a = h5 - h1 - h3; b = h3 - h1; c = h1; } } else { if (x > y) { // 3 triangle (h2, h4, h5 points) int32 h2 = V9_h1_ptr[129]; int32 h4 = V9_h1_ptr[130]; int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; a = h2 + h4 - h5; b = h4 - h2; c = h5 - h4; } else { // 4 triangle (h3, h4, h5 points) int32 h3 = V9_h1_ptr[ 1]; int32 h4 = V9_h1_ptr[130]; int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int]; a = h4 - h3; b = h3 + h4 - h5; c = h5 - h4; } } // Calculate height 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) return _liquidLevel; x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); int cx_int = ((int)x & (MAP_RESOLUTION-1)) - _liquidOffY; int cy_int = ((int)y & (MAP_RESOLUTION-1)) - _liquidOffX; if (cx_int < 0 || cx_int >=_liquidHeight) return INVALID_HEIGHT; if (cy_int < 0 || cy_int >=_liquidWidth) return INVALID_HEIGHT; return _liquidMap[cx_int*_liquidWidth + cy_int]; } // Why does this return LIQUID data? uint8 GridMap::getTerrainType(float x, float y) const { if (!_liquidFlags) return 0; x = 16 * (32 - x/SIZE_OF_GRIDS); y = 16 * (32 - y/SIZE_OF_GRIDS); int lx = (int)x & 15; int ly = (int)y & 15; return _liquidFlags[lx*16 + ly]; } // Get water state on map inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) { // Check water type (if no water return) if (!_liquidType && !_liquidFlags) return LIQUID_MAP_NO_WATER; // Get cell float cx = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS); float cy = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS); int x_int = (int)cx & (MAP_RESOLUTION-1); int y_int = (int)cy & (MAP_RESOLUTION-1); // Check water type in cell int idx=(x_int>>3)*16 + (y_int>>3); uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType; uint32 entry = 0; if (_liquidEntry) { if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx])) { entry = liquidEntry->Id; type &= MAP_LIQUID_TYPE_DARK_WATER; uint32 liqTypeIdx = liquidEntry->Type; if (entry < 21) { if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; if (!overrideLiquid && area->zone) { area = sAreaTableStore.LookupEntry(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; } if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) { entry = overrideLiquid; liqTypeIdx = liq->Type; } } } type |= 1 << liqTypeIdx; } } if (type == 0) return LIQUID_MAP_NO_WATER; // Check req liquid type mask if (ReqLiquidType && !(ReqLiquidType&type)) return LIQUID_MAP_NO_WATER; // Check water level: // Check water height map int lx_int = x_int - _liquidOffY; int ly_int = y_int - _liquidOffX; if (lx_int < 0 || lx_int >=_liquidHeight) return LIQUID_MAP_NO_WATER; if (ly_int < 0 || ly_int >=_liquidWidth) return LIQUID_MAP_NO_WATER; // Get water level float liquid_level = _liquidMap ? _liquidMap[lx_int*_liquidWidth + ly_int] : _liquidLevel; // Get ground level (sub 0.2 for fix some errors) float ground_level = getHeight(x, y); // Check water level and ground level if (liquid_level < ground_level || z < ground_level - 2) return LIQUID_MAP_NO_WATER; // All ok in water -> store data if (data) { data->entry = entry; data->type_flags = type; data->level = liquid_level; data->depth_level = ground_level; } // For speed check as int values float delta = liquid_level - z; if (delta > 2.0f) // Under water return LIQUID_MAP_UNDER_WATER; if (delta > 0.0f) // In water return LIQUID_MAP_IN_WATER; if (delta > -0.1f) // Walk on water return LIQUID_MAP_WATER_WALK; // Above water return LIQUID_MAP_ABOVE_WATER; } GridMap* Map::GetGrid(float x, float y) { // half opt method int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y // ensure GridMap is loaded EnsureGridCreated(GridCoord(63-gx, 63-gy)); return GridMaps[gx][gy]; } float Map::GetWaterOrGroundLevel(float x, float y, float z, float* ground /*= NULL*/, bool /*swim = false*/, float maxSearchDist /*= 50.0f*/) const { if (const_cast(this)->GetGrid(x, y)) { // we need ground level (including grid height version) for proper return water level in point float ground_z = GetHeight(PHASEMASK_NORMAL, x, y, z, true, maxSearchDist); if (ground) *ground = ground_z; LiquidData liquid_status; ZLiquidStatus res = getLiquidStatus(x, y, ground_z, MAP_ALL_LIQUIDS, &liquid_status); return res ? liquid_status.level : ground_z; } return VMAP_INVALID_HEIGHT_VALUE; } Transport* Map::GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject) { G3D::Vector3 v(x, y, z + 2.0f); G3D::Ray r(v, G3D::Vector3(0, 0, -1)); for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) if ((*itr)->IsInWorld() && (*itr)->GetExactDistSq(x, y, z) < 75.0f*75.0f && (*itr)->m_model) { float dist = 30.0f; bool hit = (*itr)->m_model->intersectRay(r, dist, false, phase); if (hit) return *itr; } if (worldobject) if (GameObject* staticTrans = worldobject->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_TRANSPORT, 75.0f)) if (staticTrans->m_model) { float dist = 10.0f; bool hit = staticTrans->m_model->intersectRay(r, dist, false, phase); if (hit) if (GetHeight(phase, x, y, z, true, 30.0f) < (v.z - dist + 1.0f)) return staticTrans->ToTransport(); } return NULL; } float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const { // find raw .map surface under Z coordinates float mapHeight = VMAP_INVALID_HEIGHT_VALUE; if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { float gridHeight = gmap->getHeight(x, y); // look from a bit higher pos to find the floor, ignore under surface case if (z + 2.0f > gridHeight) mapHeight = gridHeight; } float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; if (checkVMap) { VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); //if (vmgr->isHeightCalcEnabled()) // pussywizard: optimization vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f, maxSearchDist); // look from a bit higher pos to find the floor } // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT // vmapheight set for any under Z value or <= INVALID_HEIGHT if (vmapHeight > INVALID_HEIGHT) { if (mapHeight > INVALID_HEIGHT) { // we have mapheight and vmapheight and must select more appropriate // 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 || fabs(mapHeight-z) > fabs(vmapHeight-z)) return vmapHeight; else return mapHeight; // better use .map surface height } else return vmapHeight; // we have only vmapHeight (if have) } return mapHeight; // explicitly use map data } float Map::GetMinHeight(float x, float y) const { if (GridMap const* grid = const_cast(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; if (wmoEntry && atEntry) { if (atEntry->flags & AREA_FLAG_OUTSIDE) return true; if (atEntry->flags & AREA_FLAG_INSIDE) return false; } outdoor = mogpFlags&0x8; if (wmoEntry) { if (wmoEntry->Flags & 4) return true; if ((wmoEntry->Flags & 2)!=0) outdoor = false; } return outdoor; } bool Map::IsOutdoors(float x, float y, float z) const { uint32 mogpFlags; int32 adtId, rootId, groupId; // no wmo found? -> outside by default if (!GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId)) return true; AreaTableEntry const* atEntry = 0; WMOAreaTableEntry const* wmoEntry= GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); if (wmoEntry) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outStaticDebug("Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId); #endif atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); } return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); } bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const { float vmap_z = z; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); if (vmgr->getAreaInfo(GetId(), x, y, vmap_z, flags, adtId, rootId, groupId)) { // check if there's terrain between player height and object height if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { float _mapheight = gmap->getHeight(x, y); // z + 2.0f condition taken from GetHeight(), not sure if it's such a great choice... if (z + 2.0f > _mapheight && _mapheight > vmap_z) return false; } return true; } return false; } uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const { uint32 mogpFlags; int32 adtId, rootId, groupId; WMOAreaTableEntry const* wmoEntry = 0; AreaTableEntry const* atEntry = 0; bool haveAreaInfo = false; if (GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId)) { haveAreaInfo = true; wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); if (wmoEntry) atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); } uint16 areaId = 0; if (atEntry) areaId = atEntry->ID; else { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) areaId = gmap->getArea(x, y); // this used while not all *.map files generated (instances) if (!areaId) areaId = i_mapEntry->linked_zone; } if (isOutdoors) { if (haveAreaInfo) *isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); else *isOutdoors = true; } 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 { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) return gmap->getTerrainType(x, y); else return 0; } ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) const { ZLiquidStatus result = LIQUID_MAP_NO_WATER; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); float liquid_level = INVALID_HEIGHT; float ground_level = INVALID_HEIGHT; uint32 liquid_type = 0; if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type)) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDebug(LOG_FILTER_MAPS, "getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type); #endif // Check water level and ground level if (liquid_level > ground_level && z > ground_level - 2) { // All ok in water -> store data if (data) { // hardcoded in client like this if (GetId() == 530 && liquid_type == 2) liquid_type = 15; uint32 liquidFlagType = 0; if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type)) liquidFlagType = liq->Type; if (liquid_type && liquid_type < 21) { if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; if (!overrideLiquid && area->zone) { area = sAreaTableStore.LookupEntry(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; } if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) { liquid_type = overrideLiquid; liquidFlagType = liq->Type; } } } data->level = liquid_level; data->depth_level = ground_level; data->entry = liquid_type; data->type_flags = 1 << liquidFlagType; } float delta = liquid_level - z; // Get position delta if (delta > 2.0f) // Under water return LIQUID_MAP_UNDER_WATER; if (delta > 0.0f) // In water return LIQUID_MAP_IN_WATER; if (delta > -0.1f) // Walk on water return LIQUID_MAP_WATER_WALK; result = LIQUID_MAP_ABOVE_WATER; } } if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { LiquidData map_data; ZLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, &map_data); // Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER: if (map_result != LIQUID_MAP_NO_WATER && (map_data.level > ground_level)) { if (data) { // hardcoded in client like this if (GetId() == 530 && map_data.entry == 2) map_data.entry = 15; *data = map_data; } return map_result; } } return result; } float Map::GetWaterLevel(float x, float y) const { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) return gmap->getLiquidLevel(x, y); else return 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) && _dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask); } bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist) { G3D::Vector3 startPos(x1, y1, z1); G3D::Vector3 dstPos(x2, y2, z2); G3D::Vector3 resultPos; bool result = _dynamicTree.getObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist); rx = resultPos.x; ry = resultPos.y; rz = resultPos.z; return result; } float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist/*=DEFAULT_HEIGHT_SEARCH*/) const { float h1, h2; h1 = GetHeight(x, y, z, vmap, maxSearchDist); h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask); return std::max(h1, h2); } bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const { LiquidData liquid_status; LiquidData* liquid_ptr = data ? data : &liquid_status; return getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER); } bool Map::IsUnderWater(float x, float y, float z) const { return getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER; } char const* Map::GetMapName() const { return i_mapEntry ? i_mapEntry->name[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0"; } void Map::SendInitSelf(Player* player) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("Creating player data for himself %u", player->GetGUIDLow()); #endif UpdateData data; // attach to player data current transport data if (Transport* transport = player->GetTransport()) transport->BuildCreateUpdateBlockForPlayer(&data, player); // build data for self presence in world at own client (one time for map) player->BuildCreateUpdateBlockForPlayer(&data, player); // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map if (Transport* transport = player->GetTransport()) for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) if (player != (*itr) && player->HaveAtClient(*itr)) (*itr)->BuildCreateUpdateBlockForPlayer(&data, player); WorldPacket packet; data.BuildPacket(&packet); player->GetSession()->SendPacket(&packet); } void Map::SendInitTransports(Player* player) { // Hack to send out transports UpdateData transData; for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) if (*itr != player->GetTransport()) (*itr)->BuildCreateUpdateBlockForPlayer(&transData, player); WorldPacket packet; transData.BuildPacket(&packet); player->GetSession()->SendPacket(&packet); } void Map::SendRemoveTransports(Player* player) { // Hack to send out transports UpdateData transData; for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) if (*itr != player->GetTransport()) (*itr)->BuildOutOfRangeUpdateBlock(&transData); // pussywizard: remove static transports from client for (Player::ClientGUIDs::const_iterator it = player->m_clientGUIDs.begin();it != player->m_clientGUIDs.end(); ) { if (IS_TRANSPORT_GUID(*it)) { transData.AddOutOfRangeGUID(*it); it = player->m_clientGUIDs.erase(it); } else ++it; } WorldPacket packet; transData.BuildPacket(&packet); player->GetSession()->SendPacket(&packet); } inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y) { if (x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS) { sLog->outError("map::setNGrid() Invalid grid coordinates found: %d, %d!", x, y); ASSERT(false); } i_grids[x][y] = grid; } void Map::DelayedUpdate(const uint32 t_diff) { for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) { MotionTransport* transport = *_transportsUpdateIter; ++_transportsUpdateIter; if (!transport->IsInWorld()) continue; transport->DelayedUpdate(t_diff); } RemoveAllObjectsInRemoveList(); } void Map::AddObjectToRemoveList(WorldObject* obj) { ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId()); obj->CleanupsBeforeDelete(false); // remove or simplify at least cross referenced links i_objectsToRemove.insert(obj); //sLog->outDebug(LOG_FILTER_MAPS, "Object (GUID: %u TypeId: %u) added to removing list.", obj->GetGUIDLow(), obj->GetTypeId()); } void Map::AddObjectToSwitchList(WorldObject* obj, bool on) { ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId()); // i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses // the contained objects only if GetTypeId() == TYPEID_UNIT , so we can return in all other cases if (obj->GetTypeId() != TYPEID_UNIT && obj->GetTypeId() != TYPEID_GAMEOBJECT) return; std::map::iterator itr = i_objectsToSwitch.find(obj); if (itr == i_objectsToSwitch.end()) i_objectsToSwitch.insert(itr, std::make_pair(obj, on)); else if (itr->second != on) i_objectsToSwitch.erase(itr); else ASSERT(false); } void Map::RemoveAllObjectsInRemoveList() { while (!i_objectsToSwitch.empty()) { std::map::iterator itr = i_objectsToSwitch.begin(); WorldObject* obj = itr->first; bool on = itr->second; i_objectsToSwitch.erase(itr); if (!obj->IsPermanentWorldObject()) { switch (obj->GetTypeId()) { case TYPEID_UNIT: SwitchGridContainers(obj->ToCreature(), on); break; case TYPEID_GAMEOBJECT: SwitchGridContainers(obj->ToGameObject(), on); break; default: break; } } } //sLog->outDebug(LOG_FILTER_MAPS, "Object remover 1 check."); while (!i_objectsToRemove.empty()) { UNORDERED_SET::iterator itr = i_objectsToRemove.begin(); WorldObject* obj = *itr; i_objectsToRemove.erase(itr); switch (obj->GetTypeId()) { case TYPEID_CORPSE: { Corpse* corpse = ObjectAccessor::GetCorpse(*obj, obj->GetGUID()); if (!corpse) sLog->outError("Tried to delete corpse/bones %u that is not in map.", obj->GetGUIDLow()); else RemoveFromMap(corpse, true); break; } case TYPEID_DYNAMICOBJECT: RemoveFromMap((DynamicObject*)obj, true); break; case TYPEID_GAMEOBJECT: if (MotionTransport* transport = obj->ToGameObject()->ToMotionTransport()) RemoveFromMap(transport, true); else RemoveFromMap(obj->ToGameObject(), true); break; case TYPEID_UNIT: // in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call // make sure that like sources auras/etc removed before destructor start obj->ToCreature()->CleanupsBeforeDelete(); RemoveFromMap(obj->ToCreature(), true); break; default: sLog->outError("Non-grid object (TypeId: %u) is in grid object remove list, ignored.", obj->GetTypeId()); break; } } //sLog->outDebug(LOG_FILTER_MAPS, "Object remover 2 check."); } uint32 Map::GetPlayersCountExceptGMs() const { uint32 count = 0; for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) if (!itr->GetSource()->IsGameMaster()) ++count; return count; } void Map::SendToPlayers(WorldPacket const* data) const { for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) itr->GetSource()->GetSession()->SendPacket(data); } template void Map::AddToActive(T* obj) { AddToActiveHelper(obj); } template <> void Map::AddToActive(Creature* c) { AddToActiveHelper(c); } template<> void Map::AddToActive(DynamicObject* d) { AddToActiveHelper(d); } template<> void Map::AddToActive(GameObject* d) { AddToActiveHelper(d); } template void Map::RemoveFromActive(T* obj) { RemoveFromActiveHelper(obj); } template <> void Map::RemoveFromActive(Creature* c) { RemoveFromActiveHelper(c); } template<> void Map::RemoveFromActive(DynamicObject* obj) { RemoveFromActiveHelper(obj); } template<> void Map::RemoveFromActive(GameObject* obj) { RemoveFromActiveHelper(obj); } template bool Map::AddToMap(Corpse*, bool); template bool Map::AddToMap(Creature*, bool); template bool Map::AddToMap(GameObject*, bool); template bool Map::AddToMap(DynamicObject*, bool); template void Map::RemoveFromMap(Corpse*, bool); template void Map::RemoveFromMap(Creature*, bool); template void Map::RemoveFromMap(GameObject*, bool); template void Map::RemoveFromMap(DynamicObject*, bool); /* ******* Dungeon Instance Maps ******* */ InstanceMap::InstanceMap(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : Map(id, InstanceId, SpawnMode, _parent), m_resetAfterUnload(false), m_unloadWhenEmpty(false), instance_script(NULL), i_script_id(0) { //lets initialize visibility distance for dungeons InstanceMap::InitVisibilityDistance(); // the timer is started by default, and stopped when the first player joins // this make sure it gets unloaded if for some reason no player joins m_unloadTimer = std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); // pussywizard: if (IsRaid()) if (time_t resetTime = sInstanceSaveMgr->GetResetTimeFor(id, Difficulty(SpawnMode))) if (time_t extendedResetTime = sInstanceSaveMgr->GetExtendedResetTimeFor(id, Difficulty(SpawnMode))) _instanceResetPeriod = extendedResetTime - resetTime; } InstanceMap::~InstanceMap() { delete instance_script; instance_script = NULL; sInstanceSaveMgr->DeleteInstanceSaveIfNeeded(GetInstanceId(), true); } void InstanceMap::InitVisibilityDistance() { //init visibility distance for instances m_VisibleDistance = World::GetMaxVisibleDistanceInInstances(); // pussywizard: this CAN NOT exceed MAX_VISIBILITY_DISTANCE switch (GetId()) { case 429: // Dire Maul m_VisibleDistance = 175.0f; break; case 649: // Trial of the Crusader case 650: // Trial of the Champion case 595: // Culling of Startholme case 658: // Pit of Saron m_VisibleDistance = 150.0f; break; case 550: // The Eye case 578: // The Nexus: The Oculus m_VisibleDistance = 175.0f; break; case 615: // Obsidian Sanctum case 616: // Eye of Eternity case 603: // Ulduar case 668: // Halls of Reflection case 631: // Icecrown Citadel case 724: // Ruby Sanctum m_VisibleDistance = 200.0f; break; } } /* Do map specific checks to see if the player can enter */ bool InstanceMap::CanEnter(Player* player, bool loginCheck) { if (!loginCheck && player->GetMapRef().getTarget() == this) { sLog->outError("InstanceMap::CanEnter - player %s(%u) already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode()); ASSERT(false); return false; } // allow GM's to enter if (player->IsGameMaster()) return Map::CanEnter(player, loginCheck); // cannot enter if the instance is full (player cap), GMs don't count uint32 maxPlayers = GetMaxPlayers(); if (GetPlayersCountExceptGMs() >= (loginCheck ? maxPlayers+1 : maxPlayers)) { #if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS) sLog->outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName().c_str()); #endif player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); return false; } // cannot enter while an encounter is in progress on raids bool checkProgress = (IsRaid() || GetId() == 668 /*HoR*/); if (checkProgress && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress()) { player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); return false; } // xinef: dont allow LFG Group to enter other instance that is selected if (Group* group = player->GetGroup()) if (group->isLFGGroup()) if (!sLFGMgr->inLfgDungeonMap(group->GetGUID(), GetId(), GetDifficulty())) { player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAP_NOT_ALLOWED); return false; } // cannot enter if instance is in use by another party/soloer that have a permanent save in the same instance id PlayerList const &playerList = GetPlayers(); if (!playerList.isEmpty()) for (PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i) if (Player* iPlayer = i->GetSource()) { if (iPlayer == player) // login case, player already added to map continue; if (iPlayer->IsGameMaster()) // bypass GMs continue; if (!player->GetGroup()) // player has not group and there is someone inside, deny entry { player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); return false; } // player inside instance has no group or his groups is different to entering player's one, deny entry if (!iPlayer->GetGroup() || iPlayer->GetGroup() != player->GetGroup()) { player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); return false; } break; } return Map::CanEnter(player, loginCheck); } /* Do map specific checks and add the player to the map if successful. */ bool InstanceMap::AddPlayerToMap(Player* player) { if (m_resetAfterUnload) // this instance has been reset, it's not meant to be used anymore return false; if (IsDungeon()) { Group* group = player->GetGroup(); // get an instance save for the map InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(GetInstanceId()); if (!mapSave) { sLog->outError("InstanceMap::Add: InstanceSave does not exist for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); return false; } // check for existing instance binds InstancePlayerBind* playerBind = sInstanceSaveMgr->PlayerGetBoundInstance(player->GetGUIDLow(), GetId(), Difficulty(GetSpawnMode())); if (playerBind && playerBind->perm) { if (playerBind->save != mapSave) { sLog->outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d, %d, %d, %d but he is being put into instance %d, %d, %d, %d", player->GetName().c_str(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->CanReset()); return false; } } else { playerBind = sInstanceSaveMgr->PlayerBindToInstance(player->GetGUIDLow(), mapSave, false, player); // pussywizard: bind lider also if not yet bound if (Group* g = player->GetGroup()) if (g->GetLeaderGUID() != player->GetGUID()) if (!sInstanceSaveMgr->PlayerGetBoundInstance(GUID_LOPART(g->GetLeaderGUID()), mapSave->GetMapId(), mapSave->GetDifficulty())) { sInstanceSaveMgr->PlayerCreateBoundInstancesMaps(GUID_LOPART(g->GetLeaderGUID())); sInstanceSaveMgr->PlayerBindToInstance(GUID_LOPART(g->GetLeaderGUID()), mapSave, false, ObjectAccessor::FindPlayerInOrOutOfWorld(g->GetLeaderGUID())); } } // increase current instances (hourly limit) // xinef: specific instances are still limited if (!group || !group->isLFGGroup() || !group->IsLfgRandomInstance()) player->AddInstanceEnterTime(GetInstanceId(), time(NULL)); if (!playerBind->perm && !mapSave->CanReset() && (!group || !group->isLFGGroup() || !group->IsLfgRandomInstance())) { WorldPacket data(SMSG_INSTANCE_LOCK_WARNING_QUERY, 9); data << uint32(60000); data << uint32(instance_script ? instance_script->GetCompletedEncounterMask() : 0); data << uint8(0); player->GetSession()->SendPacket(&data); player->SetPendingBind(mapSave->GetInstanceId(), 60000); } } // initialize unload state m_unloadTimer = 0; m_resetAfterUnload = false; m_unloadWhenEmpty = false; // this will acquire the same mutex so it cannot be in the previous block Map::AddPlayerToMap(player); if (instance_script) instance_script->OnPlayerEnter(player); return true; } void InstanceMap::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) { Map::Update(t_diff, s_diff); if (t_diff) if (instance_script) instance_script->Update(t_diff); } void InstanceMap::RemovePlayerFromMap(Player* player, bool remove) { // pussywizard: moved m_unloadTimer to InstanceMap::AfterPlayerUnlinkFromMap(), in this function if 2 players run out at the same time the instance won't close //if (!m_unloadTimer && m_mapRefManager.getSize() == 1) // m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); Map::RemovePlayerFromMap(player, remove); } void InstanceMap::AfterPlayerUnlinkFromMap() { if (!m_unloadTimer && !HavePlayers()) m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); Map::AfterPlayerUnlinkFromMap(); } void InstanceMap::CreateInstanceScript(bool load, std::string data, uint32 completedEncounterMask) { if (instance_script != NULL) return; InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(GetId()); if (mInstance) { i_script_id = mInstance->ScriptId; instance_script = sScriptMgr->CreateInstanceScript(this); } if (!instance_script) return; instance_script->Initialize(); if (load) { instance_script->SetCompletedEncountersMask(completedEncounterMask, false); if (data != "") instance_script->Load(data.c_str()); } } /* Returns true if there are no players in the instance */ bool InstanceMap::Reset(uint8 method, std::list* globalResetSkipList) { if (method == INSTANCE_RESET_GLOBAL) { // pussywizard: teleport out immediately for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) { // teleport players that are no longer bound (can be still bound if extended id) if (!globalResetSkipList || std::find(globalResetSkipList->begin(), globalResetSkipList->end(), itr->GetSource()->GetGUIDLow()) == globalResetSkipList->end()) itr->GetSource()->RepopAtGraveyard(); } // reset map only if noone is bound if (!globalResetSkipList || globalResetSkipList->empty()) { // pussywizard: setting both m_unloadWhenEmpty and m_unloadTimer intended, in case RepopAtGraveyard failed if (HavePlayers()) m_unloadWhenEmpty = true; m_unloadTimer = MIN_UNLOAD_DELAY; m_resetAfterUnload = true; } return m_mapRefManager.isEmpty(); } if (HavePlayers()) { if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY) { for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) itr->GetSource()->SendResetFailedNotify(GetId()); } } else { m_unloadTimer = MIN_UNLOAD_DELAY; m_resetAfterUnload = true; } return m_mapRefManager.isEmpty(); } void InstanceMap::PermBindAllPlayers() { if (!IsDungeon()) return; InstanceSave* save = sInstanceSaveMgr->GetInstanceSave(GetInstanceId()); if (!save) { sLog->outError("Cannot bind players because no instance save is available for instance map (Name: %s, Entry: %u, InstanceId: %u)!", GetMapName(), GetId(), GetInstanceId()); return; } Player* player; Group* group; // group members outside the instance group don't get bound for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) { player = itr->GetSource(); group = player->GetGroup(); // players inside an instance cannot be bound to other instances // some players may already be permanently bound, in this case nothing happens InstancePlayerBind* bind = sInstanceSaveMgr->PlayerGetBoundInstance(player->GetGUIDLow(), save->GetMapId(), save->GetDifficulty()); if (!bind || !bind->perm) { WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); data << uint32(0); player->GetSession()->SendPacket(&data); sInstanceSaveMgr->PlayerBindToInstance(player->GetGUIDLow(), save, true, player); } // Xinef: Difficulty change prevention if (group) group->SetDifficultyChangePrevention(DIFFICULTY_PREVENTION_CHANGE_BOSS_KILLED); } } void InstanceMap::UnloadAll() { ASSERT(!HavePlayers()); if (m_resetAfterUnload == true) DeleteRespawnTimes(); Map::UnloadAll(); } void InstanceMap::SendResetWarnings(uint32 timeLeft) const { for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) itr->GetSource()->SendInstanceResetWarning(GetId(), itr->GetSource()->GetDifficulty(IsRaid()), timeLeft, false); } MapDifficulty const* Map::GetMapDifficulty() const { return GetMapDifficultyData(GetId(), GetDifficulty()); } uint32 InstanceMap::GetMaxPlayers() const { MapDifficulty const* mapDiff = GetMapDifficulty(); if (mapDiff && mapDiff->maxPlayers) return mapDiff->maxPlayers; return GetEntry()->maxPlayers; } uint32 InstanceMap::GetMaxResetDelay() const { MapDifficulty const* mapDiff = GetMapDifficulty(); return mapDiff ? mapDiff->resetTime : 0; } /* ******* Battleground Instance Maps ******* */ BattlegroundMap::BattlegroundMap(uint32 id, uint32 InstanceId, Map* _parent, uint8 spawnMode) : Map(id, InstanceId, spawnMode, _parent), m_bg(NULL) { //lets initialize visibility distance for BG/Arenas BattlegroundMap::InitVisibilityDistance(); } BattlegroundMap::~BattlegroundMap() { if (m_bg) { //unlink to prevent crash, always unlink all pointer reference before destruction m_bg->SetBgMap(NULL); m_bg = NULL; } } void BattlegroundMap::InitVisibilityDistance() { //init visibility distance for BG/Arenas m_VisibleDistance = World::GetMaxVisibleDistanceInBGArenas(); if (IsBattleArena()) // pussywizard: start with 30yd visibility range on arenas to ensure players can't get informations about the opponents in any way m_VisibleDistance = 30.0f; } bool BattlegroundMap::CanEnter(Player* player, bool loginCheck) { if (!loginCheck && player->GetMapRef().getTarget() == this) { sLog->outError("BGMap::CanEnter - player %u is already in map!", player->GetGUIDLow()); ASSERT(false); return false; } if (player->GetBattlegroundId() != GetInstanceId()) return false; // pussywizard: no need to check player limit here, invitations are limited by Battleground::GetFreeSlotsForTeam return Map::CanEnter(player, loginCheck); } bool BattlegroundMap::AddPlayerToMap(Player* player) { player->m_InstanceValid = true; if (IsBattleArena()) player->CastSpell(player, 100102, true); return Map::AddPlayerToMap(player); } void BattlegroundMap::RemovePlayerFromMap(Player* player, bool remove) { if (Battleground* bg = GetBG()) { bg->RemovePlayerAtLeave(player); if (IsBattleArena()) bg->RemoveSpectator(player); } if (IsBattleArena()) player->RemoveAura(100102); Map::RemovePlayerFromMap(player, remove); } void BattlegroundMap::SetUnload() { m_unloadTimer = MIN_UNLOAD_DELAY; } void BattlegroundMap::RemoveAllPlayers() { if (HavePlayers()) for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) if (Player* player = itr->GetSource()) if (!player->IsBeingTeleportedFar()) player->TeleportTo(player->GetEntryPoint()); } Player* Map::GetPlayer(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (Player*)NULL); } Creature* Map::GetCreature(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (Creature*)NULL); } GameObject* Map::GetGameObject(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (GameObject*)NULL); } Transport* Map::GetTransport(uint64 guid) { if (GUID_HIPART(guid) != HIGHGUID_MO_TRANSPORT && GUID_HIPART(guid) != HIGHGUID_TRANSPORT) return NULL; GameObject* go = GetGameObject(guid); return go ? go->ToTransport() : NULL; } DynamicObject* Map::GetDynamicObject(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (DynamicObject*)NULL); } Pet* Map::GetPet(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (Pet*)NULL); } Corpse* Map::GetCorpse(uint64 guid) { return ObjectAccessor::GetObjectInMap(guid, this, (Corpse*)NULL); } void Map::UpdateIteratorBack(Player* player) { if (m_mapRefIter == player->GetMapRef()) m_mapRefIter = m_mapRefIter->nocheck_prev(); } void Map::SaveCreatureRespawnTime(uint32 dbGuid, time_t& respawnTime) { if (!respawnTime) { // Delete only RemoveCreatureRespawnTime(dbGuid); return; } time_t now = time(NULL); if (GetInstanceResetPeriod() > 0 && respawnTime-now+5 >= GetInstanceResetPeriod()) respawnTime = now+YEAR; _creatureRespawnTimes[dbGuid] = respawnTime; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN); stmt->setUInt32(0, dbGuid); stmt->setUInt32(1, uint32(respawnTime)); stmt->setUInt16(2, GetId()); stmt->setUInt32(3, GetInstanceId()); CharacterDatabase.Execute(stmt); } void Map::RemoveCreatureRespawnTime(uint32 dbGuid) { _creatureRespawnTimes.erase(dbGuid); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); stmt->setUInt32(0, dbGuid); stmt->setUInt16(1, GetId()); stmt->setUInt32(2, GetInstanceId()); CharacterDatabase.Execute(stmt); } void Map::SaveGORespawnTime(uint32 dbGuid, time_t& respawnTime) { if (!respawnTime) { // Delete only RemoveGORespawnTime(dbGuid); return; } time_t now = time(NULL); if (GetInstanceResetPeriod() > 0 && respawnTime-now+5 >= GetInstanceResetPeriod()) respawnTime = now+YEAR; _goRespawnTimes[dbGuid] = respawnTime; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN); stmt->setUInt32(0, dbGuid); stmt->setUInt32(1, uint32(respawnTime)); stmt->setUInt16(2, GetId()); stmt->setUInt32(3, GetInstanceId()); CharacterDatabase.Execute(stmt); } void Map::RemoveGORespawnTime(uint32 dbGuid) { _goRespawnTimes.erase(dbGuid); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN); stmt->setUInt32(0, dbGuid); stmt->setUInt16(1, GetId()); stmt->setUInt32(2, GetInstanceId()); CharacterDatabase.Execute(stmt); } void Map::LoadRespawnTimes() { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CREATURE_RESPAWNS); stmt->setUInt16(0, GetId()); stmt->setUInt32(1, GetInstanceId()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { do { Field* fields = result->Fetch(); uint32 loguid = fields[0].GetUInt32(); uint32 respawnTime = fields[1].GetUInt32(); _creatureRespawnTimes[loguid] = time_t(respawnTime); } while (result->NextRow()); } stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GO_RESPAWNS); stmt->setUInt16(0, GetId()); stmt->setUInt32(1, GetInstanceId()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { do { Field* fields = result->Fetch(); uint32 loguid = fields[0].GetUInt32(); uint32 respawnTime = fields[1].GetUInt32(); _goRespawnTimes[loguid] = time_t(respawnTime); } while (result->NextRow()); } } void Map::DeleteRespawnTimes() { _creatureRespawnTimes.clear(); _goRespawnTimes.clear(); DeleteRespawnTimesInDB(GetId(), GetInstanceId()); } void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE); stmt->setUInt16(0, mapId); stmt->setUInt32(1, instanceId); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE); stmt->setUInt16(0, mapId); stmt->setUInt32(1, instanceId); CharacterDatabase.Execute(stmt); } void Map::UpdateEncounterState(EncounterCreditType type, uint32 creditEntry, Unit* source) { Difficulty difficulty_fixed = (IsSharedDifficultyMap(GetId()) ? Difficulty(GetDifficulty()%2) : GetDifficulty()); DungeonEncounterList const* encounters = sObjectMgr->GetDungeonEncounterList(GetId(), difficulty_fixed); if (!encounters) return; uint32 dungeonId = 0; for (DungeonEncounterList::const_iterator itr = encounters->begin(); itr != encounters->end(); ++itr) { DungeonEncounter const* encounter = *itr; if (encounter->creditType == type && encounter->creditEntry == creditEntry) { if (source) if (InstanceScript* instanceScript = source->GetInstanceScript()) instanceScript->SetCompletedEncountersMask((1 << encounter->dbcEntry->encounterIndex)|instanceScript->GetCompletedEncounterMask(), true); if (encounter->lastEncounterDungeon) { dungeonId = encounter->lastEncounterDungeon; break; } } } // pussywizard: LogEncounterFinished(type, creditEntry); if (dungeonId) { Map::PlayerList const& players = GetPlayers(); for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) { if (Player* player = i->GetSource()) if (Group* grp = player->GetGroup()) if (grp->isLFGGroup()) { sLFGMgr->FinishDungeon(grp->GetGUID(), dungeonId, this); return; } } } } void Map::LogEncounterFinished(EncounterCreditType type, uint32 creditEntry) { if (!IsRaid() || !GetEntry() || GetEntry()->Expansion() < 2) // only for wotlk raids, because logs take up tons of mysql memory return; InstanceMap* map = ToInstanceMap(); if (!map) return; std::string playersInfo; char buffer[16384], buffer2[255]; Map::PlayerList const& pl = map->GetPlayers(); for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) if (Player* p = itr->GetSource()) { std::string auraStr; const Unit::AuraApplicationMap& a = p->GetAppliedAuras(); for (Unit::AuraApplicationMap::const_iterator itr = a.begin(); itr != a.end(); ++itr) { snprintf(buffer2, 255, "%u(%u) ", itr->first, itr->second->GetEffectMask()); auraStr += buffer2; } snprintf(buffer, 16384, "%s (guid: %u, acc: %u, ip: %s, guild: %u), xyz: (%.1f, %.1f, %.1f), auras: %s\n", p->GetName().c_str(), p->GetGUIDLow(), p->GetSession()->GetAccountId(), p->GetSession()->GetRemoteAddress().c_str(), p->GetGuildId(), p->GetPositionX(), p->GetPositionY(), p->GetPositionZ(), auraStr.c_str()); playersInfo += buffer; } CleanStringForMysqlQuery(playersInfo); CharacterDatabase.PExecute("INSERT INTO log_encounter VALUES(NOW(), %u, %u, %u, %u, '%s')", GetId(), (uint32)GetDifficulty(), type, creditEntry, playersInfo.c_str()); } bool Map::AllTransportsEmpty() const { for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) if (!(*itr)->GetPassengers().empty()) return false; return true; } void Map::AllTransportsRemovePassengers() { for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) while (!(*itr)->GetPassengers().empty()) (*itr)->RemovePassenger(*((*itr)->GetPassengers().begin()), true); } time_t Map::GetLinkedRespawnTime(uint64 guid) const { uint64 linkedGuid = sObjectMgr->GetLinkedRespawnGuid(guid); switch (GUID_HIPART(linkedGuid)) { case HIGHGUID_UNIT: return GetCreatureRespawnTime(GUID_LOPART(linkedGuid)); case HIGHGUID_GAMEOBJECT: return GetGORespawnTime(GUID_LOPART(linkedGuid)); default: break; } return time_t(0); } void Map::SendZoneDynamicInfo(Player* player) { uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); ZoneDynamicInfoMap::const_iterator itr = _zoneDynamicInfo.find(zoneId); if (itr == _zoneDynamicInfo.end()) return; if (uint32 music = itr->second.MusicId) { WorldPacket data(SMSG_PLAY_MUSIC, 4); data << uint32(music); player->SendDirectMessage(&data); } if (uint32 weather = itr->second.WeatherId) { WorldPacket data(SMSG_WEATHER, 4 + 4 + 1); data << uint32(weather); data << float(itr->second.WeatherGrade); data << uint8(0); player->SendDirectMessage(&data); } if (uint32 overrideLight = itr->second.OverrideLightId) { WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 1); data << uint32(_defaultLight); data << uint32(overrideLight); data << uint32(itr->second.LightFadeInTime); player->SendDirectMessage(&data); } } void Map::SetZoneMusic(uint32 zoneId, uint32 musicId) { if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); _zoneDynamicInfo[zoneId].MusicId = musicId; Map::PlayerList const& players = GetPlayers(); if (!players.isEmpty()) { WorldPacket data(SMSG_PLAY_MUSIC, 4); data << uint32(musicId); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (Player* player = itr->GetSource()) if (player->GetZoneId() == zoneId) player->SendDirectMessage(&data); } } void Map::SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade) { if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId]; info.WeatherId = weatherId; info.WeatherGrade = weatherGrade; Map::PlayerList const& players = GetPlayers(); if (!players.isEmpty()) { WorldPacket data(SMSG_WEATHER, 4 + 4 + 1); data << uint32(weatherId); data << float(weatherGrade); data << uint8(0); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (Player* player = itr->GetSource()) if (player->GetZoneId() == zoneId) player->SendDirectMessage(&data); } } void Map::SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime) { if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end()) _zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo())); ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId]; info.OverrideLightId = lightId; info.LightFadeInTime = fadeInTime; Map::PlayerList const& players = GetPlayers(); if (!players.isEmpty()) { WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 4); data << uint32(_defaultLight); data << uint32(lightId); data << uint32(fadeInTime); for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) if (Player* player = itr->GetSource()) if (player->GetZoneId() == zoneId) player->SendDirectMessage(&data); } }