[7986] MaNGOS 0.13 release.
[getmangos.git] / src / game / Map.cpp
blob3882fdcfcc435cc34105ebf9f32069ff61efaad9
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "MapManager.h"
20 #include "Player.h"
21 #include "GridNotifiers.h"
22 #include "Log.h"
23 #include "GridStates.h"
24 #include "CellImpl.h"
25 #include "InstanceData.h"
26 #include "Map.h"
27 #include "GridNotifiersImpl.h"
28 #include "Config/ConfigEnv.h"
29 #include "Transports.h"
30 #include "ObjectAccessor.h"
31 #include "ObjectMgr.h"
32 #include "World.h"
33 #include "ScriptCalls.h"
34 #include "Group.h"
35 #include "MapRefManager.h"
37 #include "MapInstanced.h"
38 #include "InstanceSaveMgr.h"
39 #include "VMapFactory.h"
41 #define DEFAULT_GRID_EXPIRY 300
42 #define MAX_GRID_LOAD_TIME 50
44 GridState* si_GridStates[MAX_GRID_STATE];
46 Map::~Map()
48 UnloadAll(true);
51 bool Map::ExistMap(uint32 mapid,int gx,int gy)
53 int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
54 char* tmp = new char[len];
55 snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,gx,gy);
57 FILE *pf=fopen(tmp,"rb");
59 if(!pf)
61 sLog.outError("Check existing of map file '%s': not exist!",tmp);
62 delete[] tmp;
63 return false;
66 map_fileheader header;
67 fread(&header, sizeof(header), 1, pf);
68 if (header.mapMagic != uint32(MAP_MAGIC) ||
69 header.versionMagic != uint32(MAP_VERSION_MAGIC))
71 sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
72 delete [] tmp;
73 fclose(pf); //close file before return
74 return false;
77 delete [] tmp;
78 fclose(pf);
79 return true;
82 bool Map::ExistVMap(uint32 mapid,int gx,int gy)
84 if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
86 if(vmgr->isMapLoadingEnabled())
88 // x and y are swapped !! => fixed now
89 bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, gx,gy);
90 if(!exists)
92 std::string name = vmgr->getDirFileName(mapid,gx,gy);
93 sLog.outError("VMap file '%s' is missing or point to wrong version vmap file, redo vmaps with latest vmap_assembler.exe program", (sWorld.GetDataPath()+"vmaps/"+name).c_str());
94 return false;
99 return true;
102 void Map::LoadVMap(int gx,int gy)
104 // x and y are swapped !!
105 int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), gx,gy);
106 switch(vmapLoadResult)
108 case VMAP::VMAP_LOAD_RESULT_OK:
109 sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy);
110 break;
111 case VMAP::VMAP_LOAD_RESULT_ERROR:
112 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);
113 break;
114 case VMAP::VMAP_LOAD_RESULT_IGNORED:
115 DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx,gy,gx,gy);
116 break;
120 void Map::LoadMap(int gx,int gy, bool reload)
122 if( i_InstanceId != 0 )
124 if(GridMaps[gx][gy])
125 return;
127 Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(i_id));
129 // load grid map for base map
130 if (!baseMap->GridMaps[gx][gy])
131 baseMap->EnsureGridCreated(GridPair(63-gx,63-gy));
133 ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(gx,gy));
134 GridMaps[gx][gy] = baseMap->GridMaps[gx][gy];
135 return;
138 if(GridMaps[gx][gy] && !reload)
139 return;
141 //map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?)
142 if(GridMaps[gx][gy])
144 sLog.outDetail("Unloading already loaded map %u before reloading.",i_id);
145 delete (GridMaps[gx][gy]);
146 GridMaps[gx][gy]=NULL;
149 // map file name
150 char *tmp=NULL;
151 int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
152 tmp = new char[len];
153 snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),i_id,gx,gy);
154 sLog.outDetail("Loading map %s",tmp);
155 // loading data
156 GridMaps[gx][gy] = new GridMap();
157 if (!GridMaps[gx][gy]->loadData(tmp))
159 sLog.outError("Error load map file: \n %s\n", tmp);
161 delete [] tmp;
164 void Map::LoadMapAndVMap(int gx,int gy)
166 LoadMap(gx,gy);
167 if(i_InstanceId == 0)
168 LoadVMap(gx, gy); // Only load the data for the base map
171 void Map::InitStateMachine()
173 si_GridStates[GRID_STATE_INVALID] = new InvalidState;
174 si_GridStates[GRID_STATE_ACTIVE] = new ActiveState;
175 si_GridStates[GRID_STATE_IDLE] = new IdleState;
176 si_GridStates[GRID_STATE_REMOVAL] = new RemovalState;
179 void Map::DeleteStateMachine()
181 delete si_GridStates[GRID_STATE_INVALID];
182 delete si_GridStates[GRID_STATE_ACTIVE];
183 delete si_GridStates[GRID_STATE_IDLE];
184 delete si_GridStates[GRID_STATE_REMOVAL];
187 Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
188 : i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode),
189 i_id(id), i_InstanceId(InstanceId), m_unloadTimer(0),
190 m_activeNonPlayersIter(m_activeNonPlayers.end()),
191 i_gridExpiry(expiry)
193 for(unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
195 for(unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j)
197 //z code
198 GridMaps[idx][j] =NULL;
199 setNGrid(NULL, idx, j);
204 // Template specialization of utility methods
205 template<class T>
206 void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell)
208 (*grid)(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj, obj->GetGUID());
211 template<>
212 void Map::AddToGrid(Player* obj, NGridType *grid, Cell const& cell)
214 (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
217 template<>
218 void Map::AddToGrid(Corpse *obj, NGridType *grid, Cell const& cell)
220 // add to world object registry in grid
221 if(obj->GetType()!=CORPSE_BONES)
223 (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
225 // add to grid object store
226 else
228 (*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID());
232 template<>
233 void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell)
235 // add to world object registry in grid
236 if(obj->isPet() || obj->isVehicle())
238 (*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID());
239 obj->SetCurrentCell(cell);
241 // add to grid object store
242 else
244 (*grid)(cell.CellX(), cell.CellY()).AddGridObject<Creature>(obj, obj->GetGUID());
245 obj->SetCurrentCell(cell);
249 template<class T>
250 void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell)
252 (*grid)(cell.CellX(), cell.CellY()).template RemoveGridObject<T>(obj, obj->GetGUID());
255 template<>
256 void Map::RemoveFromGrid(Player* obj, NGridType *grid, Cell const& cell)
258 (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
261 template<>
262 void Map::RemoveFromGrid(Corpse *obj, NGridType *grid, Cell const& cell)
264 // remove from world object registry in grid
265 if(obj->GetType()!=CORPSE_BONES)
267 (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
269 // remove from grid object store
270 else
272 (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject(obj, obj->GetGUID());
276 template<>
277 void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell)
279 // remove from world object registry in grid
280 if(obj->isPet() || obj->isVehicle())
282 (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID());
284 // remove from grid object store
285 else
287 (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject<Creature>(obj, obj->GetGUID());
291 template<class T>
292 void Map::DeleteFromWorld(T* obj)
294 // Note: In case resurrectable corpse and pet its removed from global lists in own destructor
295 delete obj;
298 template<class T>
299 void Map::AddNotifier(T* , Cell const& , CellPair const& )
303 template<>
304 void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair)
306 PlayerRelocationNotify(obj,cell,cellpair);
309 template<>
310 void Map::AddNotifier(Creature* obj, Cell const& cell, CellPair const& cellpair)
312 CreatureRelocationNotify(obj,cell,cellpair);
315 void
316 Map::EnsureGridCreated(const GridPair &p)
318 if(!getNGrid(p.x_coord, p.y_coord))
320 Guard guard(*this);
321 if(!getNGrid(p.x_coord, p.y_coord))
323 setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, sWorld.getConfig(CONFIG_GRID_UNLOAD)),
324 p.x_coord, p.y_coord);
326 // build a linkage between this map and NGridType
327 buildNGridLinkage(getNGrid(p.x_coord, p.y_coord));
329 getNGrid(p.x_coord, p.y_coord)->SetGridState(GRID_STATE_IDLE);
331 //z coord
332 int gx=63-p.x_coord;
333 int gy=63-p.y_coord;
335 if(!GridMaps[gx][gy])
336 LoadMapAndVMap(gx,gy);
341 void
342 Map::EnsureGridLoadedAtEnter(const Cell &cell, Player *player)
344 NGridType *grid;
346 if(EnsureGridLoaded(cell))
348 grid = getNGrid(cell.GridX(), cell.GridY());
350 if (player)
352 player->SendDelayResponse(MAX_GRID_LOAD_TIME);
353 DEBUG_LOG("Player %s enter cell[%u,%u] triggers of loading grid[%u,%u] on map %u", player->GetName(), cell.CellX(), cell.CellY(), cell.GridX(), cell.GridY(), i_id);
355 else
357 DEBUG_LOG("Active object nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id);
360 ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f);
361 grid->SetGridState(GRID_STATE_ACTIVE);
363 else
364 grid = getNGrid(cell.GridX(), cell.GridY());
366 if (player)
367 AddToGrid(player,grid,cell);
370 bool Map::EnsureGridLoaded(const Cell &cell)
372 EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
373 NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
375 assert(grid != NULL);
376 if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
378 ObjectGridLoader loader(*grid, this, cell);
379 loader.LoadN();
381 // Add resurrectable corpses to world object list in grid
382 ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
384 setGridObjectDataLoaded(true,cell.GridX(), cell.GridY());
385 return true;
388 return false;
391 void Map::LoadGrid(const Cell& cell, bool no_unload)
393 EnsureGridLoaded(cell);
395 if(no_unload)
396 getNGrid(cell.GridX(), cell.GridY())->setUnloadExplicitLock(true);
399 bool Map::Add(Player *player)
401 player->GetMapRef().link(this, player);
403 player->SetInstanceId(GetInstanceId());
405 // update player state for other player and visa-versa
406 CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
407 Cell cell(p);
408 EnsureGridLoadedAtEnter(cell, player);
409 player->AddToWorld();
411 SendInitSelf(player);
412 SendInitTransports(player);
414 UpdatePlayerVisibility(player,cell,p);
415 UpdateObjectsVisibilityFor(player,cell,p);
417 AddNotifier(player,cell,p);
418 return true;
421 template<class T>
422 void
423 Map::Add(T *obj)
425 CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
427 assert(obj);
429 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
431 sLog.outError("Map::Add: Object (GUID: %u TypeId: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
432 return;
435 Cell cell(p);
436 if(obj->isActiveObject())
437 EnsureGridLoadedAtEnter(cell);
438 else
439 EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
441 NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
442 assert( grid != NULL );
444 AddToGrid(obj,grid,cell);
445 obj->AddToWorld();
447 if(obj->isActiveObject())
448 AddToActive(obj);
450 DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY());
452 UpdateObjectVisibility(obj,cell,p);
454 AddNotifier(obj,cell,p);
457 void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self)
459 CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
461 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
463 sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
464 return;
467 Cell cell(p);
468 cell.data.Part.reserved = ALL_DISTRICT;
470 if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
471 return;
473 MaNGOS::MessageDeliverer post_man(*player, msg, to_self);
474 TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > message(post_man);
475 CellLock<ReadGuard> cell_lock(cell, p);
476 cell_lock->Visit(cell_lock, message, *this);
479 void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg)
481 CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
483 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
485 sLog.outError("Map::MessageBroadcast: Object (GUID: %u TypeId: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
486 return;
489 Cell cell(p);
490 cell.data.Part.reserved = ALL_DISTRICT;
491 cell.SetNoCreate();
493 if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
494 return;
496 MaNGOS::ObjectMessageDeliverer post_man(*obj,msg);
497 TypeContainerVisitor<MaNGOS::ObjectMessageDeliverer, WorldTypeMapContainer > message(post_man);
498 CellLock<ReadGuard> cell_lock(cell, p);
499 cell_lock->Visit(cell_lock, message, *this);
502 void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only)
504 CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
506 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
508 sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
509 return;
512 Cell cell(p);
513 cell.data.Part.reserved = ALL_DISTRICT;
515 if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
516 return;
518 MaNGOS::MessageDistDeliverer post_man(*player, msg, dist, to_self, own_team_only);
519 TypeContainerVisitor<MaNGOS::MessageDistDeliverer , WorldTypeMapContainer > message(post_man);
520 CellLock<ReadGuard> cell_lock(cell, p);
521 cell_lock->Visit(cell_lock, message, *this);
524 void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist)
526 CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
528 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
530 sLog.outError("Map::MessageBroadcast: Object (GUID: %u TypeId: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
531 return;
534 Cell cell(p);
535 cell.data.Part.reserved = ALL_DISTRICT;
536 cell.SetNoCreate();
538 if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
539 return;
541 MaNGOS::ObjectMessageDistDeliverer post_man(*obj, msg,dist);
542 TypeContainerVisitor<MaNGOS::ObjectMessageDistDeliverer, WorldTypeMapContainer > message(post_man);
543 CellLock<ReadGuard> cell_lock(cell, p);
544 cell_lock->Visit(cell_lock, message, *this);
547 bool Map::loaded(const GridPair &p) const
549 return ( getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord) );
552 void Map::Update(const uint32 &t_diff)
554 resetMarkedCells();
556 MaNGOS::ObjectUpdater updater(t_diff);
557 // for creature
558 TypeContainerVisitor<MaNGOS::ObjectUpdater, GridTypeMapContainer > grid_object_update(updater);
559 // for pets
560 TypeContainerVisitor<MaNGOS::ObjectUpdater, WorldTypeMapContainer > world_object_update(updater);
562 // the player iterator is stored in the map object
563 // to make sure calls to Map::Remove don't invalidate it
564 for(m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
566 Player* plr = m_mapRefIter->getSource();
568 if(!plr->IsInWorld())
569 continue;
571 CellPair standing_cell(MaNGOS::ComputeCellPair(plr->GetPositionX(), plr->GetPositionY()));
573 // Check for correctness of standing_cell, it also avoids problems with update_cell
574 if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
575 continue;
577 // the overloaded operators handle range checking
578 // so ther's no need for range checking inside the loop
579 CellPair begin_cell(standing_cell), end_cell(standing_cell);
580 begin_cell << 1; begin_cell -= 1; // upper left
581 end_cell >> 1; end_cell += 1; // lower right
583 for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; ++x)
585 for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; ++y)
587 // marked cells are those that have been visited
588 // don't visit the same cell twice
589 uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
590 if(!isCellMarked(cell_id))
592 markCell(cell_id);
593 CellPair pair(x,y);
594 Cell cell(pair);
595 cell.data.Part.reserved = CENTER_DISTRICT;
596 cell.SetNoCreate();
597 CellLock<NullGuard> cell_lock(cell, pair);
598 cell_lock->Visit(cell_lock, grid_object_update, *this);
599 cell_lock->Visit(cell_lock, world_object_update, *this);
605 // non-player active objects
606 if(!m_activeNonPlayers.empty())
608 for(m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end(); )
610 // skip not in world
611 WorldObject* obj = *m_activeNonPlayersIter;
613 // step before processing, in this case if Map::Remove remove next object we correctly
614 // step to next-next, and if we step to end() then newly added objects can wait next update.
615 ++m_activeNonPlayersIter;
617 if(!obj->IsInWorld())
618 continue;
620 CellPair standing_cell(MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()));
622 // Check for correctness of standing_cell, it also avoids problems with update_cell
623 if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
624 continue;
626 // the overloaded operators handle range checking
627 // so ther's no need for range checking inside the loop
628 CellPair begin_cell(standing_cell), end_cell(standing_cell);
629 begin_cell << 1; begin_cell -= 1; // upper left
630 end_cell >> 1; end_cell += 1; // lower right
632 for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; ++x)
634 for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; ++y)
636 // marked cells are those that have been visited
637 // don't visit the same cell twice
638 uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
639 if(!isCellMarked(cell_id))
641 markCell(cell_id);
642 CellPair pair(x,y);
643 Cell cell(pair);
644 cell.data.Part.reserved = CENTER_DISTRICT;
645 cell.SetNoCreate();
646 CellLock<NullGuard> cell_lock(cell, pair);
647 cell_lock->Visit(cell_lock, grid_object_update, *this);
648 cell_lock->Visit(cell_lock, world_object_update, *this);
655 // Don't unload grids if it's battleground, since we may have manually added GOs,creatures, those doesn't load from DB at grid re-load !
656 // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended
657 if (IsBattleGroundOrArena())
658 return;
660 for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
662 NGridType *grid = i->getSource();
663 GridInfo *info = i->getSource()->getGridInfoRef();
664 ++i; // The update might delete the map and we need the next map before the iterator gets invalid
665 assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE);
666 si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff);
670 void Map::Remove(Player *player, bool remove)
672 // this may be called during Map::Update
673 // after decrement+unlink, ++m_mapRefIter will continue correctly
674 // when the first element of the list is being removed
675 // nocheck_prev will return the padding element of the RefManager
676 // instead of NULL in the case of prev
677 if(m_mapRefIter == player->GetMapRef())
678 m_mapRefIter = m_mapRefIter->nocheck_prev();
679 player->GetMapRef().unlink();
680 CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
681 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
683 // invalid coordinates
684 player->RemoveFromWorld();
686 if( remove )
687 DeleteFromWorld(player);
689 return;
692 Cell cell(p);
694 if( !getNGrid(cell.data.Part.grid_x, cell.data.Part.grid_y) )
696 sLog.outError("Map::Remove() i_grids was NULL x:%d, y:%d",cell.data.Part.grid_x,cell.data.Part.grid_y);
697 return;
700 DEBUG_LOG("Remove player %s from grid[%u,%u]", player->GetName(), cell.GridX(), cell.GridY());
701 NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
702 assert(grid != NULL);
704 player->RemoveFromWorld();
705 RemoveFromGrid(player,grid,cell);
707 SendRemoveTransports(player);
709 UpdateObjectsVisibilityFor(player,cell,p);
711 if( remove )
712 DeleteFromWorld(player);
715 bool Map::RemoveBones(uint64 guid, float x, float y)
717 if (IsRemovalGrid(x, y))
719 Corpse * corpse = ObjectAccessor::Instance().GetObjectInWorld(GetId(), x, y, guid, (Corpse*)NULL);
720 if(corpse && corpse->GetTypeId() == TYPEID_CORPSE && corpse->GetType() == CORPSE_BONES)
721 corpse->DeleteBonesFromWorld();
722 else
723 return false;
725 return true;
728 template<class T>
729 void
730 Map::Remove(T *obj, bool remove)
732 CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
733 if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
735 sLog.outError("Map::Remove: Object (GUID: %u TypeId:%u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
736 return;
739 Cell cell(p);
740 if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
741 return;
743 DEBUG_LOG("Remove object (GUID: %u TypeId:%u) from grid[%u,%u]", obj->GetGUIDLow(), obj->GetTypeId(), cell.data.Part.grid_x, cell.data.Part.grid_y);
744 NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
745 assert( grid != NULL );
747 if(obj->isActiveObject())
748 RemoveFromActive(obj);
750 obj->RemoveFromWorld();
751 RemoveFromGrid(obj,grid,cell);
753 UpdateObjectVisibility(obj,cell,p);
755 if( remove )
757 // if option set then object already saved at this moment
758 if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
759 obj->SaveRespawnTime();
760 DeleteFromWorld(obj);
764 void
765 Map::PlayerRelocation(Player *player, float x, float y, float z, float orientation)
767 assert(player);
769 CellPair old_val = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
770 CellPair new_val = MaNGOS::ComputeCellPair(x, y);
772 Cell old_cell(old_val);
773 Cell new_cell(new_val);
774 new_cell |= old_cell;
775 bool same_cell = (new_cell == old_cell);
777 player->Relocate(x, y, z, orientation);
779 if( old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell) )
781 DEBUG_LOG("Player %s relocation grid[%u,%u]cell[%u,%u]->grid[%u,%u]cell[%u,%u]", player->GetName(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
783 // update player position for group at taxi flight
784 if(player->GetGroup() && player->isInFlight())
785 player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
787 NGridType* oldGrid = getNGrid(old_cell.GridX(), old_cell.GridY());
788 RemoveFromGrid(player, oldGrid,old_cell);
789 if( !old_cell.DiffGrid(new_cell) )
790 AddToGrid(player, oldGrid,new_cell);
791 else
792 EnsureGridLoadedAtEnter(new_cell, player);
795 // if move then update what player see and who seen
796 UpdatePlayerVisibility(player,new_cell,new_val);
797 UpdateObjectsVisibilityFor(player,new_cell,new_val);
798 PlayerRelocationNotify(player,new_cell,new_val);
799 NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY());
800 if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE )
802 ResetGridExpiry(*newGrid, 0.1f);
803 newGrid->SetGridState(GRID_STATE_ACTIVE);
807 void
808 Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang)
810 assert(CheckGridIntegrity(creature,false));
812 Cell old_cell = creature->GetCurrentCell();
814 CellPair new_val = MaNGOS::ComputeCellPair(x, y);
815 Cell new_cell(new_val);
817 // delay creature move for grid/cell to grid/cell moves
818 if( old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell) )
820 #ifdef MANGOS_DEBUG
821 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
822 sLog.outDebug("Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
823 #endif
824 AddCreatureToMoveList(creature,x,y,z,ang);
825 // in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList
827 else
829 creature->Relocate(x, y, z, ang);
830 CreatureRelocationNotify(creature,new_cell,new_val);
832 assert(CheckGridIntegrity(creature,true));
835 void Map::AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang)
837 if(!c)
838 return;
840 i_creaturesToMove[c] = CreatureMover(x,y,z,ang);
843 void Map::MoveAllCreaturesInMoveList()
845 while(!i_creaturesToMove.empty())
847 // get data and remove element;
848 CreatureMoveList::iterator iter = i_creaturesToMove.begin();
849 Creature* c = iter->first;
850 CreatureMover cm = iter->second;
851 i_creaturesToMove.erase(iter);
853 // calculate cells
854 CellPair new_val = MaNGOS::ComputeCellPair(cm.x, cm.y);
855 Cell new_cell(new_val);
857 // do move or do move to respawn or remove creature if previous all fail
858 if(CreatureCellRelocation(c,new_cell))
860 // update pos
861 c->Relocate(cm.x, cm.y, cm.z, cm.ang);
862 CreatureRelocationNotify(c,new_cell,new_cell.cellPair());
864 else
866 // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
867 // creature coordinates will be updated and notifiers send
868 if(!CreatureRespawnRelocation(c))
870 // ... or unload (if respawn grid also not loaded)
871 #ifdef MANGOS_DEBUG
872 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
873 sLog.outDebug("Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",c->GetGUIDLow(),c->GetEntry());
874 #endif
875 c->CleanupsBeforeDelete();
876 AddObjectToRemoveList(c);
882 bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
884 Cell const& old_cell = c->GetCurrentCell();
885 if(!old_cell.DiffGrid(new_cell) ) // in same grid
887 // if in same cell then none do
888 if(old_cell.DiffCell(new_cell))
890 #ifdef MANGOS_DEBUG
891 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
892 sLog.outDebug("Creature (GUID: %u Entry: %u) moved in grid[%u,%u] from cell[%u,%u] to cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY());
893 #endif
895 if( !old_cell.DiffGrid(new_cell) )
897 RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
898 AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
899 c->SetCurrentCell(new_cell);
902 else
904 #ifdef MANGOS_DEBUG
905 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
906 sLog.outDebug("Creature (GUID: %u Entry: %u) move in same grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY());
907 #endif
910 return true;
913 // in diff. grids but active creature
914 if(c->isActiveObject())
916 EnsureGridLoadedAtEnter(new_cell);
918 #ifdef MANGOS_DEBUG
919 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
920 sLog.outDebug("Active creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
921 #endif
923 RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
924 AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
926 return true;
929 // in diff. loaded grid normal creature
930 if(loaded(GridPair(new_cell.GridX(), new_cell.GridY())))
932 #ifdef MANGOS_DEBUG
933 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
934 sLog.outDebug("Creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
935 #endif
937 RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
939 EnsureGridCreated(GridPair(new_cell.GridX(), new_cell.GridY()));
940 AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
943 return true;
946 // fail to move: normal creature attempt move to unloaded grid
947 #ifdef MANGOS_DEBUG
948 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
949 sLog.outDebug("Creature (GUID: %u Entry: %u) attempt move from grid[%u,%u]cell[%u,%u] to unloaded grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
950 #endif
951 return false;
954 bool Map::CreatureRespawnRelocation(Creature *c)
956 float resp_x, resp_y, resp_z, resp_o;
957 c->GetRespawnCoord(resp_x, resp_y, resp_z, &resp_o);
959 CellPair resp_val = MaNGOS::ComputeCellPair(resp_x, resp_y);
960 Cell resp_cell(resp_val);
962 c->CombatStop();
963 c->GetMotionMaster()->Clear();
965 #ifdef MANGOS_DEBUG
966 if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
967 sLog.outDebug("Creature (GUID: %u Entry: %u) will moved from grid[%u,%u]cell[%u,%u] to respawn grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), c->GetCurrentCell().GridX(), c->GetCurrentCell().GridY(), c->GetCurrentCell().CellX(), c->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY());
968 #endif
970 // teleport it to respawn point (like normal respawn if player see)
971 if(CreatureCellRelocation(c,resp_cell))
973 c->Relocate(resp_x, resp_y, resp_z, resp_o);
974 c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators
975 CreatureRelocationNotify(c,resp_cell,resp_cell.cellPair());
976 return true;
978 else
979 return false;
982 bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool pForce)
984 NGridType *grid = getNGrid(x, y);
985 assert( grid != NULL);
988 if(!pForce && ActiveObjectsNearGrid(x, y) )
989 return false;
991 DEBUG_LOG("Unloading grid[%u,%u] for map %u", x,y, i_id);
992 ObjectGridUnloader unloader(*grid);
994 // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
995 // Must know real mob position before move
996 DoDelayedMovesAndRemoves();
998 // move creatures to respawn grids if this is diff.grid or to remove list
999 unloader.MoveToRespawnN();
1001 // Finish creature moves, remove and delete all creatures with delayed remove before unload
1002 DoDelayedMovesAndRemoves();
1004 unloader.UnloadN();
1005 delete getNGrid(x, y);
1006 setNGrid(NULL, x, y);
1008 int gx=63-x;
1009 int gy=63-y;
1011 // delete grid map, but don't delete if it is from parent map (and thus only reference)
1012 //+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps
1014 if (i_InstanceId == 0)
1016 if(GridMaps[gx][gy])
1018 GridMaps[gx][gy]->unloadData();
1019 delete GridMaps[gx][gy];
1021 // x and y are swapped
1022 VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx);
1024 else
1025 ((MapInstanced*)(MapManager::Instance().GetBaseMap(i_id)))->RemoveGridMapReference(GridPair(gx, gy));
1026 GridMaps[gx][gy] = NULL;
1028 DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, i_id);
1029 return true;
1032 void Map::UnloadAll(bool pForce)
1034 // clear all delayed moves, useless anyway do this moves before map unload.
1035 i_creaturesToMove.clear();
1037 for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
1039 NGridType &grid(*i->getSource());
1040 ++i;
1041 UnloadGrid(grid.getX(), grid.getY(), pForce); // deletes the grid and removes it from the GridRefManager
1045 //*****************************
1046 // Grid function
1047 //*****************************
1048 GridMap::GridMap()
1050 m_flags = 0;
1051 // Area data
1052 m_gridArea = 0;
1053 m_area_map = NULL;
1054 // Height level data
1055 m_gridHeight = INVALID_HEIGHT;
1056 m_gridGetHeight = &GridMap::getHeightFromFlat;
1057 m_V9 = NULL;
1058 m_V8 = NULL;
1059 // Liquid data
1060 m_liquidType = 0;
1061 m_liquid_offX = 0;
1062 m_liquid_offY = 0;
1063 m_liquid_width = 0;
1064 m_liquid_height = 0;
1065 m_liquidLevel = INVALID_HEIGHT;
1066 m_liquid_type = NULL;
1067 m_liquid_map = NULL;
1070 GridMap::~GridMap()
1072 unloadData();
1075 bool GridMap::loadData(char *filename)
1077 // Unload old data if exist
1078 unloadData();
1080 map_fileheader header;
1081 // Not return error if file not found
1082 FILE *in = fopen(filename, "rb");
1083 if (!in)
1084 return true;
1085 fread(&header, sizeof(header),1,in);
1086 if (header.mapMagic == uint32(MAP_MAGIC) &&
1087 header.versionMagic == uint32(MAP_VERSION_MAGIC))
1089 // loadup area data
1090 if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize))
1092 sLog.outError("Error loading map area data\n");
1093 fclose(in);
1094 return false;
1096 // loadup height data
1097 if (header.heightMapOffset && !loadHeihgtData(in, header.heightMapOffset, header.heightMapSize))
1099 sLog.outError("Error loading map height data\n");
1100 fclose(in);
1101 return false;
1103 // loadup liquid data
1104 if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize))
1106 sLog.outError("Error loading map liquids data\n");
1107 fclose(in);
1108 return false;
1110 fclose(in);
1111 return true;
1113 sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.", filename);
1114 fclose(in);
1115 return false;
1118 void GridMap::unloadData()
1120 if (m_area_map) delete[] m_area_map;
1121 if (m_V9) delete[] m_V9;
1122 if (m_V8) delete[] m_V8;
1123 if (m_liquid_type) delete[] m_liquid_type;
1124 if (m_liquid_map) delete[] m_liquid_map;
1125 m_area_map = NULL;
1126 m_V9 = NULL;
1127 m_V8 = NULL;
1128 m_liquid_type = NULL;
1129 m_liquid_map = NULL;
1130 m_gridGetHeight = &GridMap::getHeightFromFlat;
1133 bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size)
1135 map_areaHeader header;
1136 fseek(in, offset, SEEK_SET);
1137 fread(&header, sizeof(header), 1, in);
1138 if (header.fourcc != uint32(MAP_AREA_MAGIC))
1139 return false;
1141 m_gridArea = header.gridArea;
1142 if (!(header.flags & MAP_AREA_NO_AREA))
1144 m_area_map = new uint16 [16*16];
1145 fread(m_area_map, sizeof(uint16), 16*16, in);
1147 return true;
1150 bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size)
1152 map_heightHeader header;
1153 fseek(in, offset, SEEK_SET);
1154 fread(&header, sizeof(header), 1, in);
1155 if (header.fourcc != uint32(MAP_HEIGTH_MAGIC))
1156 return false;
1158 m_gridHeight = header.gridHeight;
1159 if (!(header.flags & MAP_HEIGHT_NO_HIGHT))
1161 if ((header.flags & MAP_HEIGHT_AS_INT16))
1163 m_uint16_V9 = new uint16 [129*129];
1164 m_uint16_V8 = new uint16 [128*128];
1165 fread(m_uint16_V9, sizeof(uint16), 129*129, in);
1166 fread(m_uint16_V8, sizeof(uint16), 128*128, in);
1167 m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535;
1168 m_gridGetHeight = &GridMap::getHeightFromUint16;
1170 else if ((header.flags & MAP_HEIGHT_AS_INT8))
1172 m_uint8_V9 = new uint8 [129*129];
1173 m_uint8_V8 = new uint8 [128*128];
1174 fread(m_uint8_V9, sizeof(uint8), 129*129, in);
1175 fread(m_uint8_V8, sizeof(uint8), 128*128, in);
1176 m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 255;
1177 m_gridGetHeight = &GridMap::getHeightFromUint8;
1179 else
1181 m_V9 = new float [129*129];
1182 m_V8 = new float [128*128];
1183 fread(m_V9, sizeof(float), 129*129, in);
1184 fread(m_V8, sizeof(float), 128*128, in);
1185 m_gridGetHeight = &GridMap::getHeightFromFloat;
1188 else
1189 m_gridGetHeight = &GridMap::getHeightFromFlat;
1190 return true;
1193 bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size)
1195 map_liquidHeader header;
1196 fseek(in, offset, SEEK_SET);
1197 fread(&header, sizeof(header), 1, in);
1198 if (header.fourcc != uint32(MAP_LIQUID_MAGIC))
1199 return false;
1201 m_liquidType = header.liquidType;
1202 m_liquid_offX = header.offsetX;
1203 m_liquid_offY = header.offsetY;
1204 m_liquid_width = header.width;
1205 m_liquid_height= header.height;
1206 m_liquidLevel = header.liquidLevel;
1208 if (!(header.flags&MAP_LIQUID_NO_TYPE))
1210 m_liquid_type = new uint8 [16*16];
1211 fread(m_liquid_type, sizeof(uint8), 16*16, in);
1213 if (!(header.flags&MAP_LIQUID_NO_HIGHT))
1215 m_liquid_map = new float [m_liquid_width*m_liquid_height];
1216 fread(m_liquid_map, sizeof(float), m_liquid_width*m_liquid_height, in);
1218 return true;
1221 uint16 GridMap::getArea(float x, float y)
1223 if (!m_area_map)
1224 return m_gridArea;
1226 x = 16 * (32 - x/SIZE_OF_GRIDS);
1227 y = 16 * (32 - y/SIZE_OF_GRIDS);
1228 int lx = (int)x & 15;
1229 int ly = (int)y & 15;
1230 return m_area_map[lx*16 + ly];
1233 float GridMap::getHeightFromFlat(float /*x*/, float /*y*/) const
1235 return m_gridHeight;
1238 float GridMap::getHeightFromFloat(float x, float y) const
1240 if (!m_V8 || !m_V9)
1241 return m_gridHeight;
1243 x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
1244 y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
1246 int x_int = (int)x;
1247 int y_int = (int)y;
1248 x -= x_int;
1249 y -= y_int;
1250 x_int&=(MAP_RESOLUTION - 1);
1251 y_int&=(MAP_RESOLUTION - 1);
1253 // Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid
1254 // +--------------> X
1255 // | h1-------h2 Coordinates is:
1256 // | | \ 1 / | h1 0,0
1257 // | | \ / | h2 0,1
1258 // | | 2 h5 3 | h3 1,0
1259 // | | / \ | h4 1,1
1260 // | | / 4 \ | h5 1/2,1/2
1261 // | h3-------h4
1262 // V Y
1263 // For find height need
1264 // 1 - detect triangle
1265 // 2 - solve linear equation from triangle points
1266 // Calculate coefficients for solve h = a*x + b*y + c
1268 float a,b,c;
1269 // Select triangle:
1270 if (x+y < 1)
1272 if (x > y)
1274 // 1 triangle (h1, h2, h5 points)
1275 float h1 = m_V9[(x_int )*129 + y_int];
1276 float h2 = m_V9[(x_int+1)*129 + y_int];
1277 float h5 = 2 * m_V8[x_int*128 + y_int];
1278 a = h2-h1;
1279 b = h5-h1-h2;
1280 c = h1;
1282 else
1284 // 2 triangle (h1, h3, h5 points)
1285 float h1 = m_V9[x_int*129 + y_int ];
1286 float h3 = m_V9[x_int*129 + y_int+1];
1287 float h5 = 2 * m_V8[x_int*128 + y_int];
1288 a = h5 - h1 - h3;
1289 b = h3 - h1;
1290 c = h1;
1293 else
1295 if (x > y)
1297 // 3 triangle (h2, h4, h5 points)
1298 float h2 = m_V9[(x_int+1)*129 + y_int ];
1299 float h4 = m_V9[(x_int+1)*129 + y_int+1];
1300 float h5 = 2 * m_V8[x_int*128 + y_int];
1301 a = h2 + h4 - h5;
1302 b = h4 - h2;
1303 c = h5 - h4;
1305 else
1307 // 4 triangle (h3, h4, h5 points)
1308 float h3 = m_V9[(x_int )*129 + y_int+1];
1309 float h4 = m_V9[(x_int+1)*129 + y_int+1];
1310 float h5 = 2 * m_V8[x_int*128 + y_int];
1311 a = h4 - h3;
1312 b = h3 + h4 - h5;
1313 c = h5 - h4;
1316 // Calculate height
1317 return a * x + b * y + c;
1320 float GridMap::getHeightFromUint8(float x, float y) const
1322 if (!m_uint8_V8 || !m_uint8_V9)
1323 return m_gridHeight;
1325 x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
1326 y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
1328 int x_int = (int)x;
1329 int y_int = (int)y;
1330 x -= x_int;
1331 y -= y_int;
1332 x_int&=(MAP_RESOLUTION - 1);
1333 y_int&=(MAP_RESOLUTION - 1);
1335 int32 a, b, c;
1336 uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int];
1337 if (x+y < 1)
1339 if (x > y)
1341 // 1 triangle (h1, h2, h5 points)
1342 int32 h1 = V9_h1_ptr[ 0];
1343 int32 h2 = V9_h1_ptr[129];
1344 int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
1345 a = h2-h1;
1346 b = h5-h1-h2;
1347 c = h1;
1349 else
1351 // 2 triangle (h1, h3, h5 points)
1352 int32 h1 = V9_h1_ptr[0];
1353 int32 h3 = V9_h1_ptr[1];
1354 int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
1355 a = h5 - h1 - h3;
1356 b = h3 - h1;
1357 c = h1;
1360 else
1362 if (x > y)
1364 // 3 triangle (h2, h4, h5 points)
1365 int32 h2 = V9_h1_ptr[129];
1366 int32 h4 = V9_h1_ptr[130];
1367 int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
1368 a = h2 + h4 - h5;
1369 b = h4 - h2;
1370 c = h5 - h4;
1372 else
1374 // 4 triangle (h3, h4, h5 points)
1375 int32 h3 = V9_h1_ptr[ 1];
1376 int32 h4 = V9_h1_ptr[130];
1377 int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
1378 a = h4 - h3;
1379 b = h3 + h4 - h5;
1380 c = h5 - h4;
1383 // Calculate height
1384 return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight;
1387 float GridMap::getHeightFromUint16(float x, float y) const
1389 if (!m_uint16_V8 || !m_uint16_V9)
1390 return m_gridHeight;
1392 x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
1393 y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
1395 int x_int = (int)x;
1396 int y_int = (int)y;
1397 x -= x_int;
1398 y -= y_int;
1399 x_int&=(MAP_RESOLUTION - 1);
1400 y_int&=(MAP_RESOLUTION - 1);
1402 int32 a, b, c;
1403 uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int];
1404 if (x+y < 1)
1406 if (x > y)
1408 // 1 triangle (h1, h2, h5 points)
1409 int32 h1 = V9_h1_ptr[ 0];
1410 int32 h2 = V9_h1_ptr[129];
1411 int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
1412 a = h2-h1;
1413 b = h5-h1-h2;
1414 c = h1;
1416 else
1418 // 2 triangle (h1, h3, h5 points)
1419 int32 h1 = V9_h1_ptr[0];
1420 int32 h3 = V9_h1_ptr[1];
1421 int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
1422 a = h5 - h1 - h3;
1423 b = h3 - h1;
1424 c = h1;
1427 else
1429 if (x > y)
1431 // 3 triangle (h2, h4, h5 points)
1432 int32 h2 = V9_h1_ptr[129];
1433 int32 h4 = V9_h1_ptr[130];
1434 int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
1435 a = h2 + h4 - h5;
1436 b = h4 - h2;
1437 c = h5 - h4;
1439 else
1441 // 4 triangle (h3, h4, h5 points)
1442 int32 h3 = V9_h1_ptr[ 1];
1443 int32 h4 = V9_h1_ptr[130];
1444 int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
1445 a = h4 - h3;
1446 b = h3 + h4 - h5;
1447 c = h5 - h4;
1450 // Calculate height
1451 return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight;
1454 float GridMap::getLiquidLevel(float x, float y)
1456 if (!m_liquid_map)
1457 return m_liquidLevel;
1459 x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
1460 y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
1462 int cx_int = ((int)x & (MAP_RESOLUTION-1)) - m_liquid_offY;
1463 int cy_int = ((int)y & (MAP_RESOLUTION-1)) - m_liquid_offX;
1465 if (cx_int < 0 || cx_int >=m_liquid_height)
1466 return INVALID_HEIGHT;
1467 if (cy_int < 0 || cy_int >=m_liquid_width )
1468 return INVALID_HEIGHT;
1470 return m_liquid_map[cx_int*m_liquid_width + cy_int];
1473 uint8 GridMap::getTerrainType(float x, float y)
1475 if (!m_liquid_type)
1476 return m_liquidType;
1478 x = 16 * (32 - x/SIZE_OF_GRIDS);
1479 y = 16 * (32 - y/SIZE_OF_GRIDS);
1480 int lx = (int)x & 15;
1481 int ly = (int)y & 15;
1482 return m_liquid_type[lx*16 + ly];
1485 // Get water state on map
1486 inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data)
1488 // Check water type (if no water return)
1489 if (!m_liquid_type && !m_liquidType)
1490 return LIQUID_MAP_NO_WATER;
1492 // Get cell
1493 float cx = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
1494 float cy = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
1496 int x_int = (int)cx & (MAP_RESOLUTION-1);
1497 int y_int = (int)cy & (MAP_RESOLUTION-1);
1499 // Check water type in cell
1500 uint8 type = m_liquid_type ? m_liquid_type[(x_int>>3)*16 + (y_int>>3)] : m_liquidType;
1501 if (type == 0)
1502 return LIQUID_MAP_NO_WATER;
1504 // Check req liquid type mask
1505 if (ReqLiquidType && !(ReqLiquidType&type))
1506 return LIQUID_MAP_NO_WATER;
1508 // Check water level:
1509 // Check water height map
1510 int lx_int = x_int - m_liquid_offY;
1511 int ly_int = y_int - m_liquid_offX;
1512 if (lx_int < 0 || lx_int >=m_liquid_height)
1513 return LIQUID_MAP_NO_WATER;
1514 if (ly_int < 0 || ly_int >=m_liquid_width )
1515 return LIQUID_MAP_NO_WATER;
1517 // Get water level
1518 float liquid_level = m_liquid_map ? m_liquid_map[lx_int*m_liquid_width + ly_int] : m_liquidLevel;
1519 // Get ground level (sub 0.2 for fix some errors)
1520 float ground_level = getHeight(x, y);
1522 // Check water level and ground level
1523 if (liquid_level < ground_level || z < ground_level - 2)
1524 return LIQUID_MAP_NO_WATER;
1526 // All ok in water -> store data
1527 if (data)
1529 data->type = type;
1530 data->level = liquid_level;
1531 data->depth_level = ground_level;
1534 // For speed check as int values
1535 int delta = int((liquid_level - z) * 10);
1537 // Get position delta
1538 if (delta > 20) // Under water
1539 return LIQUID_MAP_UNDER_WATER;
1540 if (delta > 0 ) // In water
1541 return LIQUID_MAP_IN_WATER;
1542 if (delta > -1) // Walk on water
1543 return LIQUID_MAP_WATER_WALK;
1544 // Above water
1545 return LIQUID_MAP_ABOVE_WATER;
1548 inline GridMap *Map::GetGrid(float x, float y)
1550 // half opt method
1551 int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x
1552 int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
1554 // ensure GridMap is loaded
1555 EnsureGridCreated(GridPair(63-gx,63-gy));
1557 return GridMaps[gx][gy];
1560 float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
1562 // find raw .map surface under Z coordinates
1563 float mapHeight;
1564 if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
1566 float _mapheight = gmap->getHeight(x,y);
1568 // look from a bit higher pos to find the floor, ignore under surface case
1569 if(z + 2.0f > _mapheight)
1570 mapHeight = _mapheight;
1571 else
1572 mapHeight = VMAP_INVALID_HEIGHT_VALUE;
1574 else
1575 mapHeight = VMAP_INVALID_HEIGHT_VALUE;
1577 float vmapHeight;
1578 if(pUseVmaps)
1580 VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
1581 if(vmgr->isHeightCalcEnabled())
1583 // look from a bit higher pos to find the floor
1584 vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f);
1586 else
1587 vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
1589 else
1590 vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
1592 // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
1593 // vmapheight set for any under Z value or <= INVALID_HEIGHT
1595 if( vmapHeight > INVALID_HEIGHT )
1597 if( mapHeight > INVALID_HEIGHT )
1599 // we have mapheight and vmapheight and must select more appropriate
1601 // we are already under the surface or vmap height above map heigt
1602 // or if the distance of the vmap height is less the land height distance
1603 if( z < mapHeight || vmapHeight > mapHeight || fabs(mapHeight-z) > fabs(vmapHeight-z) )
1604 return vmapHeight;
1605 else
1606 return mapHeight; // better use .map surface height
1609 else
1610 return vmapHeight; // we have only vmapHeight (if have)
1612 else
1614 if(!pUseVmaps)
1615 return mapHeight; // explicitly use map data (if have)
1616 else if(mapHeight > INVALID_HEIGHT && (z < mapHeight + 2 || z == MAX_HEIGHT))
1617 return mapHeight; // explicitly use map data if original z < mapHeight but map found (z+2 > mapHeight)
1618 else
1619 return VMAP_INVALID_HEIGHT_VALUE; // we not have any height
1623 uint16 Map::GetAreaFlag(float x, float y, float z) const
1625 uint16 areaflag;
1626 if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
1627 areaflag = gmap->getArea(x, y);
1628 // this used while not all *.map files generated (instances)
1629 else
1630 areaflag = GetAreaFlagByMapId(i_id);
1632 //FIXME: some hacks for areas above or underground for ground area
1633 // required for area specific spells/etc, until map/vmap data
1634 // not provided correct areaflag with this hacks
1635 switch(areaflag)
1637 // Acherus: The Ebon Hold (Plaguelands: The Scarlet Enclave)
1638 case 1984: // Plaguelands: The Scarlet Enclave
1639 case 2076: // Death's Breach (Plaguelands: The Scarlet Enclave)
1640 case 2745: // The Noxious Pass (Plaguelands: The Scarlet Enclave)
1641 if(z > 350.0f) areaflag = 2048; break;
1642 // Acherus: The Ebon Hold (Eastern Plaguelands)
1643 case 856: // The Noxious Glade (Eastern Plaguelands)
1644 case 2456: // Death's Breach (Eastern Plaguelands)
1645 if(z > 350.0f) areaflag = 1950; break;
1646 // Dalaran
1647 case 2492: // Forlorn Woods (Crystalsong Forest)
1648 if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f)
1650 // Krasus' Landing (Dalaran), fast check
1651 if (x > 5758.77f && x < 5869.03f && y < 510.46f)
1653 // Krasus' Landing (Dalaran), with open east side
1654 if (y < 449.33f || (x-5813.9f)*(x-5813.9f)+(y-449.33f)*(y-449.33f) < 1864.0f)
1656 areaflag = 2533; // Note: also 2633, possible one flight allowed and other not allowed case
1657 break;
1661 // Dalaran
1662 areaflag = 2153;
1664 break;
1665 // The Violet Citadel (Dalaran) or Dalaran
1666 case 2484: // The Twilight Rivulet (Crystalsong Forest)
1667 case 1593: // Crystalsong Forest
1668 // Dalaran
1669 if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f)
1671 // The Violet Citadel (Dalaran), fast check
1672 if (x > 5721.1f && x < 5884.66f && y > 764.4f && y < 948.0f)
1674 // The Violet Citadel (Dalaran)
1675 if ((x-5803.0f)*(x-5803.0f)+(y-846.18f)*(y-846.18f) < 6690.0f)
1677 areaflag = 2696;
1678 break;
1682 // Dalaran
1683 areaflag = 2153;
1685 break;
1686 // Vargoth's Retreat (Dalaran) or The Violet Citadel (Dalaran) or Dalaran
1687 case 2504: // Violet Stand (Crystalsong Forest)
1688 // Dalaran
1689 if (x > 5568.0f && x < 6116.0f && y > 282.0f && y < 982.0f && z > 563.0f)
1691 // The Violet Citadel (Dalaran), fast check
1692 if (x > 5721.1f && x < 5884.66f && y > 764.4f && y < 948.0f)
1694 // Vargoth's Retreat (Dalaran), nice slow circle with upper limit
1695 if (z < 898.0f && (x-5765.0f)*(x-5765.0f)+(y-862.4f)*(y-862.4f) < 262.0f)
1697 areaflag = 2748;
1698 break;
1701 // The Violet Citadel (Dalaran)
1702 if ((x-5803.0f)*(x-5803.0f)+(y-846.18f)*(y-846.18f) < 6690.0f)
1704 areaflag = 2696;
1705 break;
1709 // Dalaran
1710 areaflag = 2153;
1712 break;
1713 // Maw of Neltharion (cave)
1714 case 164: // Dragonblight
1715 case 1797: // Obsidian Dragonshrine (Dragonblight)
1716 case 1827: // Wintergrasp
1717 case 2591: // The Cauldron of Flames (Wintergrasp)
1718 if (x > 4364.0f && x < 4632.0f && y > 1545.0f && y < 1886.0f && z < 200.0f) areaflag = 1853; break;
1719 // Undercity (sewers enter and path)
1720 case 179: // Tirisfal Glades
1721 if (x > 1595.0f && x < 1699.0f && y > 535.0f && y < 643.5f && z < 30.5f) areaflag = 685; break;
1722 // Undercity (Royal Quarter)
1723 case 210: // Silverpine Forest
1724 case 316: // The Shining Strand (Silverpine Forest)
1725 case 438: // Lordamere Lake (Silverpine Forest)
1726 if (x > 1237.0f && x < 1401.0f && y > 284.0f && y < 440.0f && z < -40.0f) areaflag = 685; break;
1727 // Undercity (cave and ground zone, part of royal quarter)
1728 case 607: // Ruins of Lordaeron (Tirisfal Glades)
1729 // ground and near to ground (by city walls)
1730 if(z > 0.0f)
1732 if (x > 1510.0f && x < 1839.0f && y > 29.77f && y < 433.0f) areaflag = 685;
1734 // more wide underground, part of royal quarter
1735 else
1737 if (x > 1299.0f && x < 1839.0f && y > 10.0f && y < 440.0f) areaflag = 685;
1739 break;
1740 // The Makers' Perch (ground) and Makers' Overlook (ground and cave)
1741 case 1335: // Sholazar Basin
1742 // The Makers' Perch ground (fast box)
1743 if (x > 6100.0f && x < 6250.0f && y > 5650.0f && y < 5800.0f)
1745 // nice slow circle
1746 if ((x-6183.0f)*(x-6183.0f)+(y-5717.0f)*(y-5717.0f) < 2500.0f)
1747 areaflag = 2189;
1749 // Makers' Overlook (ground and cave)
1750 else if (x > 5634.48f && x < 5774.53f && y < 3475.0f && z > 300.0f)
1752 if(y > 3380.26f || y > 3265.0f && z < 360.0f) areaflag = 2187;
1754 break;
1755 // The Makers' Perch (underground)
1756 case 2147: // The Stormwright's Shelf (Sholazar Basin)
1757 if (x > 6199.0f && x < 6283.0f && y > 5705.0f && y < 5817.0f && z < 38.0f) areaflag = 2189; break;
1758 // Makers' Overlook (deep cave)
1759 case 267: // Icecrown
1760 if (x > 5684.0f && x < 5798.0f && y > 3035.0f && y < 3367.0f && z < 358.0f) areaflag = 2187; break;
1761 // Wyrmrest Temple (Dragonblight)
1762 case 1814: // Path of the Titans (Dragonblight)
1763 case 1897: // The Dragon Wastes (Dragonblight)
1764 // fast box
1765 if (x > 3400.0f && x < 3700.0f && y > 130.0f && y < 420.0f)
1767 // nice slow circle
1768 if ((x-3546.87f)*(x-3546.87f)+(y-272.71f)*(y-272.71f) < 19600.0f) areaflag = 1791;
1770 break;
1773 return areaflag;
1776 uint8 Map::GetTerrainType(float x, float y ) const
1778 if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
1779 return gmap->getTerrainType(x, y);
1780 else
1781 return 0;
1784 ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) const
1786 if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
1787 return gmap->getLiquidStatus(x, y, z, ReqLiquidType, data);
1788 else
1789 return LIQUID_MAP_NO_WATER;
1792 float Map::GetWaterLevel(float x, float y ) const
1794 if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
1795 return gmap->getLiquidLevel(x, y);
1796 else
1797 return 0;
1800 uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id)
1802 AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
1804 if (entry)
1805 return entry->ID;
1806 else
1807 return 0;
1810 uint32 Map::GetZoneIdByAreaFlag(uint16 areaflag,uint32 map_id)
1812 AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
1814 if( entry )
1815 return ( entry->zone != 0 ) ? entry->zone : entry->ID;
1816 else
1817 return 0;
1820 void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag,uint32 map_id)
1822 AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
1824 areaid = entry ? entry->ID : 0;
1825 zoneid = entry ? (( entry->zone != 0 ) ? entry->zone : entry->ID) : 0;
1828 bool Map::IsInWater(float x, float y, float pZ) const
1830 // Check surface in x, y point for liquid
1831 if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
1833 LiquidData liquid_status;
1834 if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, &liquid_status))
1836 if (liquid_status.level - liquid_status.depth_level > 2)
1837 return true;
1840 return false;
1843 bool Map::IsUnderWater(float x, float y, float z) const
1845 if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
1847 if (getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN)&LIQUID_MAP_UNDER_WATER)
1848 return true;
1850 return false;
1853 bool Map::CheckGridIntegrity(Creature* c, bool moved) const
1855 Cell const& cur_cell = c->GetCurrentCell();
1857 CellPair xy_val = MaNGOS::ComputeCellPair(c->GetPositionX(), c->GetPositionY());
1858 Cell xy_cell(xy_val);
1859 if(xy_cell != cur_cell)
1861 sLog.outError("Creature (GUIDLow: %u) X: %f Y: %f (%s) in grid[%u,%u]cell[%u,%u] instead grid[%u,%u]cell[%u,%u]",
1862 c->GetGUIDLow(),
1863 c->GetPositionX(),c->GetPositionY(),(moved ? "final" : "original"),
1864 cur_cell.GridX(), cur_cell.GridY(), cur_cell.CellX(), cur_cell.CellY(),
1865 xy_cell.GridX(), xy_cell.GridY(), xy_cell.CellX(), xy_cell.CellY());
1866 return true; // not crash at error, just output error in debug mode
1869 return true;
1872 const char* Map::GetMapName() const
1874 return i_mapEntry ? i_mapEntry->name[sWorld.GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
1877 void Map::UpdateObjectVisibility( WorldObject* obj, Cell cell, CellPair cellpair)
1879 cell.data.Part.reserved = ALL_DISTRICT;
1880 cell.SetNoCreate();
1881 MaNGOS::VisibleChangesNotifier notifier(*obj);
1882 TypeContainerVisitor<MaNGOS::VisibleChangesNotifier, WorldTypeMapContainer > player_notifier(notifier);
1883 CellLock<GridReadGuard> cell_lock(cell, cellpair);
1884 cell_lock->Visit(cell_lock, player_notifier, *this);
1887 void Map::UpdatePlayerVisibility( Player* player, Cell cell, CellPair cellpair )
1889 cell.data.Part.reserved = ALL_DISTRICT;
1891 MaNGOS::PlayerNotifier pl_notifier(*player);
1892 TypeContainerVisitor<MaNGOS::PlayerNotifier, WorldTypeMapContainer > player_notifier(pl_notifier);
1894 CellLock<ReadGuard> cell_lock(cell, cellpair);
1895 cell_lock->Visit(cell_lock, player_notifier, *this);
1898 void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair )
1900 MaNGOS::VisibleNotifier notifier(*player);
1902 cell.data.Part.reserved = ALL_DISTRICT;
1903 cell.SetNoCreate();
1904 TypeContainerVisitor<MaNGOS::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
1905 TypeContainerVisitor<MaNGOS::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
1906 CellLock<GridReadGuard> cell_lock(cell, cellpair);
1907 cell_lock->Visit(cell_lock, world_notifier, *this);
1908 cell_lock->Visit(cell_lock, grid_notifier, *this);
1910 // send data
1911 notifier.Notify();
1914 void Map::PlayerRelocationNotify( Player* player, Cell cell, CellPair cellpair )
1916 CellLock<ReadGuard> cell_lock(cell, cellpair);
1917 MaNGOS::PlayerRelocationNotifier relocationNotifier(*player);
1918 cell.data.Part.reserved = ALL_DISTRICT;
1920 TypeContainerVisitor<MaNGOS::PlayerRelocationNotifier, GridTypeMapContainer > p2grid_relocation(relocationNotifier);
1921 TypeContainerVisitor<MaNGOS::PlayerRelocationNotifier, WorldTypeMapContainer > p2world_relocation(relocationNotifier);
1923 cell_lock->Visit(cell_lock, p2grid_relocation, *this);
1924 cell_lock->Visit(cell_lock, p2world_relocation, *this);
1927 void Map::CreatureRelocationNotify(Creature *creature, Cell cell, CellPair cellpair)
1929 CellLock<ReadGuard> cell_lock(cell, cellpair);
1930 MaNGOS::CreatureRelocationNotifier relocationNotifier(*creature);
1931 cell.data.Part.reserved = ALL_DISTRICT;
1932 cell.SetNoCreate(); // not trigger load unloaded grids at notifier call
1934 TypeContainerVisitor<MaNGOS::CreatureRelocationNotifier, WorldTypeMapContainer > c2world_relocation(relocationNotifier);
1935 TypeContainerVisitor<MaNGOS::CreatureRelocationNotifier, GridTypeMapContainer > c2grid_relocation(relocationNotifier);
1937 cell_lock->Visit(cell_lock, c2world_relocation, *this);
1938 cell_lock->Visit(cell_lock, c2grid_relocation, *this);
1941 void Map::SendInitSelf( Player * player )
1943 sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow());
1945 UpdateData data;
1947 // attach to player data current transport data
1948 if(Transport* transport = player->GetTransport())
1950 transport->BuildCreateUpdateBlockForPlayer(&data, player);
1953 // build data for self presence in world at own client (one time for map)
1954 player->BuildCreateUpdateBlockForPlayer(&data, player);
1956 // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
1957 if(Transport* transport = player->GetTransport())
1959 for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr)
1961 if(player!=(*itr) && player->HaveAtClient(*itr))
1963 (*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
1968 WorldPacket packet;
1969 data.BuildPacket(&packet);
1970 player->GetSession()->SendPacket(&packet);
1973 void Map::SendInitTransports( Player * player )
1975 // Hack to send out transports
1976 MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
1978 // no transports at map
1979 if (tmap.find(player->GetMapId()) == tmap.end())
1980 return;
1982 UpdateData transData;
1984 MapManager::TransportSet& tset = tmap[player->GetMapId()];
1986 for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i)
1988 // send data for current transport in other place
1989 if((*i) != player->GetTransport() && (*i)->GetMapId()==i_id)
1991 (*i)->BuildCreateUpdateBlockForPlayer(&transData, player);
1995 WorldPacket packet;
1996 transData.BuildPacket(&packet);
1997 player->GetSession()->SendPacket(&packet);
2000 void Map::SendRemoveTransports( Player * player )
2002 // Hack to send out transports
2003 MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
2005 // no transports at map
2006 if (tmap.find(player->GetMapId()) == tmap.end())
2007 return;
2009 UpdateData transData;
2011 MapManager::TransportSet& tset = tmap[player->GetMapId()];
2013 // except used transport
2014 for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i)
2015 if((*i) != player->GetTransport() && (*i)->GetMapId()!=i_id)
2016 (*i)->BuildOutOfRangeUpdateBlock(&transData);
2018 WorldPacket packet;
2019 transData.BuildPacket(&packet);
2020 player->GetSession()->SendPacket(&packet);
2023 inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y)
2025 if(x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS)
2027 sLog.outError("map::setNGrid() Invalid grid coordinates found: %d, %d!",x,y);
2028 assert(false);
2030 i_grids[x][y] = grid;
2033 void Map::DoDelayedMovesAndRemoves()
2035 MoveAllCreaturesInMoveList();
2036 RemoveAllObjectsInRemoveList();
2039 void Map::AddObjectToRemoveList(WorldObject *obj)
2041 assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId());
2043 i_objectsToRemove.insert(obj);
2044 //sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId());
2047 void Map::RemoveAllObjectsInRemoveList()
2049 if(i_objectsToRemove.empty())
2050 return;
2052 //sLog.outDebug("Object remover 1 check.");
2053 while(!i_objectsToRemove.empty())
2055 WorldObject* obj = *i_objectsToRemove.begin();
2056 i_objectsToRemove.erase(i_objectsToRemove.begin());
2058 switch(obj->GetTypeId())
2060 case TYPEID_CORPSE:
2062 Corpse* corpse = ObjectAccessor::Instance().GetCorpse(*obj, obj->GetGUID());
2063 if (!corpse)
2064 sLog.outError("Try delete corpse/bones %u that not in map", obj->GetGUIDLow());
2065 else
2066 Remove(corpse,true);
2067 break;
2069 case TYPEID_DYNAMICOBJECT:
2070 Remove((DynamicObject*)obj,true);
2071 break;
2072 case TYPEID_GAMEOBJECT:
2073 Remove((GameObject*)obj,true);
2074 break;
2075 case TYPEID_UNIT:
2076 // in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call
2077 // make sure that like sources auras/etc removed before destructor start
2078 ((Creature*)obj)->CleanupsBeforeDelete ();
2079 Remove((Creature*)obj,true);
2080 break;
2081 default:
2082 sLog.outError("Non-grid object (TypeId: %u) in grid object removing list, ignored.",obj->GetTypeId());
2083 break;
2086 //sLog.outDebug("Object remover 2 check.");
2089 uint32 Map::GetPlayersCountExceptGMs() const
2091 uint32 count = 0;
2092 for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2093 if(!itr->getSource()->isGameMaster())
2094 ++count;
2095 return count;
2098 void Map::SendToPlayers(WorldPacket const* data) const
2100 for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2101 itr->getSource()->GetSession()->SendPacket(data);
2104 bool Map::ActiveObjectsNearGrid(uint32 x, uint32 y) const
2106 CellPair cell_min(x*MAX_NUMBER_OF_CELLS, y*MAX_NUMBER_OF_CELLS);
2107 CellPair cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord+MAX_NUMBER_OF_CELLS);
2108 cell_min << 2;
2109 cell_min -= 2;
2110 cell_max >> 2;
2111 cell_max += 2;
2113 for(MapRefManager::const_iterator iter = m_mapRefManager.begin(); iter != m_mapRefManager.end(); ++iter)
2115 Player* plr = iter->getSource();
2117 CellPair p = MaNGOS::ComputeCellPair(plr->GetPositionX(), plr->GetPositionY());
2118 if( (cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) &&
2119 (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord) )
2120 return true;
2123 for(ActiveNonPlayers::const_iterator iter = m_activeNonPlayers.begin(); iter != m_activeNonPlayers.end(); ++iter)
2125 WorldObject* obj = *iter;
2127 CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
2128 if( (cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) &&
2129 (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord) )
2130 return true;
2133 return false;
2136 void Map::AddToActive( Creature* c )
2138 AddToActiveHelper(c);
2140 // also not allow unloading spawn grid to prevent creating creature clone at load
2141 if(!c->isPet() && c->GetDBTableGUIDLow())
2143 float x,y,z;
2144 c->GetRespawnCoord(x,y,z);
2145 GridPair p = MaNGOS::ComputeGridPair(x, y);
2146 if(getNGrid(p.x_coord, p.y_coord))
2147 getNGrid(p.x_coord, p.y_coord)->incUnloadActiveLock();
2148 else
2150 GridPair p2 = MaNGOS::ComputeGridPair(c->GetPositionX(), c->GetPositionY());
2151 sLog.outError("Active creature (GUID: %u Entry: %u) added to grid[%u,%u] but spawn grid[%u,%u] not loaded.",
2152 c->GetGUIDLow(), c->GetEntry(), p.x_coord, p.y_coord, p2.x_coord, p2.y_coord);
2157 void Map::RemoveFromActive( Creature* c )
2159 RemoveFromActiveHelper(c);
2161 // also allow unloading spawn grid
2162 if(!c->isPet() && c->GetDBTableGUIDLow())
2164 float x,y,z;
2165 c->GetRespawnCoord(x,y,z);
2166 GridPair p = MaNGOS::ComputeGridPair(x, y);
2167 if(getNGrid(p.x_coord, p.y_coord))
2168 getNGrid(p.x_coord, p.y_coord)->decUnloadActiveLock();
2169 else
2171 GridPair p2 = MaNGOS::ComputeGridPair(c->GetPositionX(), c->GetPositionY());
2172 sLog.outError("Active creature (GUID: %u Entry: %u) removed from grid[%u,%u] but spawn grid[%u,%u] not loaded.",
2173 c->GetGUIDLow(), c->GetEntry(), p.x_coord, p.y_coord, p2.x_coord, p2.y_coord);
2178 template void Map::Add(Corpse *);
2179 template void Map::Add(Creature *);
2180 template void Map::Add(GameObject *);
2181 template void Map::Add(DynamicObject *);
2183 template void Map::Remove(Corpse *,bool);
2184 template void Map::Remove(Creature *,bool);
2185 template void Map::Remove(GameObject *, bool);
2186 template void Map::Remove(DynamicObject *, bool);
2188 /* ******* Dungeon Instance Maps ******* */
2190 InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
2191 : Map(id, expiry, InstanceId, SpawnMode),
2192 m_resetAfterUnload(false), m_unloadWhenEmpty(false),
2193 i_data(NULL), i_script_id(0)
2195 // the timer is started by default, and stopped when the first player joins
2196 // this make sure it gets unloaded if for some reason no player joins
2197 m_unloadTimer = std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
2200 InstanceMap::~InstanceMap()
2202 if(i_data)
2204 delete i_data;
2205 i_data = NULL;
2210 Do map specific checks to see if the player can enter
2212 bool InstanceMap::CanEnter(Player *player)
2214 if(player->GetMapRef().getTarget() == this)
2216 sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
2217 assert(false);
2218 return false;
2221 // cannot enter if the instance is full (player cap), GMs don't count
2222 uint32 maxPlayers = GetMaxPlayers();
2223 if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= maxPlayers)
2225 sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName());
2226 player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
2227 return false;
2230 // cannot enter while players in the instance are in combat
2231 Group *pGroup = player->GetGroup();
2232 if(pGroup && pGroup->InCombatToInstance(GetInstanceId()) && player->isAlive() && player->GetMapId() != GetId())
2234 player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
2235 return false;
2238 return Map::CanEnter(player);
2242 Do map specific checks and add the player to the map if successful.
2244 bool InstanceMap::Add(Player *player)
2246 // TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode
2247 // GMs still can teleport player in instance.
2248 // Is it needed?
2251 Guard guard(*this);
2252 if(!CanEnter(player))
2253 return false;
2255 // Dungeon only code
2256 if(IsDungeon())
2258 // get or create an instance save for the map
2259 InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
2260 if(!mapSave)
2262 sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId());
2263 mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true);
2266 // check for existing instance binds
2267 InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode());
2268 if(playerBind && playerBind->perm)
2270 // cannot enter other instances if bound permanently
2271 if(playerBind->save != mapSave)
2273 sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset());
2274 assert(false);
2277 else
2279 Group *pGroup = player->GetGroup();
2280 if(pGroup)
2282 // solo saves should be reset when entering a group
2283 InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode());
2284 if(playerBind)
2286 sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
2287 if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
2288 assert(false);
2290 // bind to the group or keep using the group save
2291 if(!groupBind)
2292 pGroup->BindToInstance(mapSave, false);
2293 else
2295 // cannot jump to a different instance without resetting it
2296 if(groupBind->save != mapSave)
2298 sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty());
2299 if(mapSave)
2300 sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
2301 else
2302 sLog.outError("MapSave NULL");
2303 if(groupBind->save)
2304 sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
2305 else
2306 sLog.outError("GroupBind save NULL");
2307 assert(false);
2309 // if the group/leader is permanently bound to the instance
2310 // players also become permanently bound when they enter
2311 if(groupBind->perm)
2313 WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
2314 data << uint32(0);
2315 player->GetSession()->SendPacket(&data);
2316 player->BindToInstance(mapSave, true);
2320 else
2322 // set up a solo bind or continue using it
2323 if(!playerBind)
2324 player->BindToInstance(mapSave, false);
2325 else
2326 // cannot jump to a different instance without resetting it
2327 assert(playerBind->save == mapSave);
2332 if(i_data) i_data->OnPlayerEnter(player);
2333 // for normal instances cancel the reset schedule when the
2334 // first player enters (no players yet)
2335 SetResetSchedule(false);
2337 sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName());
2338 // initialize unload state
2339 m_unloadTimer = 0;
2340 m_resetAfterUnload = false;
2341 m_unloadWhenEmpty = false;
2344 // this will acquire the same mutex so it cannot be in the previous block
2345 Map::Add(player);
2346 return true;
2349 void InstanceMap::Update(const uint32& t_diff)
2351 Map::Update(t_diff);
2353 if(i_data)
2354 i_data->Update(t_diff);
2357 void InstanceMap::Remove(Player *player, bool remove)
2359 sLog.outDetail("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
2360 //if last player set unload timer
2361 if(!m_unloadTimer && m_mapRefManager.getSize() == 1)
2362 m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
2363 Map::Remove(player, remove);
2364 // for normal instances schedule the reset after all players have left
2365 SetResetSchedule(true);
2368 void InstanceMap::CreateInstanceData(bool load)
2370 if(i_data != NULL)
2371 return;
2373 InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(GetId());
2374 if (mInstance)
2376 i_script_id = mInstance->script_id;
2377 i_data = Script->CreateInstanceData(this);
2380 if(!i_data)
2381 return;
2383 if(load)
2385 // TODO: make a global storage for this
2386 QueryResult* result = CharacterDatabase.PQuery("SELECT data FROM instance WHERE map = '%u' AND id = '%u'", GetId(), i_InstanceId);
2387 if (result)
2389 Field* fields = result->Fetch();
2390 const char* data = fields[0].GetString();
2391 if(data)
2393 sLog.outDebug("Loading instance data for `%s` with id %u", objmgr.GetScriptName(i_script_id), i_InstanceId);
2394 i_data->Load(data);
2396 delete result;
2399 else
2401 sLog.outDebug("New instance data, \"%s\" ,initialized!", objmgr.GetScriptName(i_script_id));
2402 i_data->Initialize();
2407 Returns true if there are no players in the instance
2409 bool InstanceMap::Reset(uint8 method)
2411 // note: since the map may not be loaded when the instance needs to be reset
2412 // the instance must be deleted from the DB by InstanceSaveManager
2414 if(HavePlayers())
2416 if(method == INSTANCE_RESET_ALL)
2418 // notify the players to leave the instance so it can be reset
2419 for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2420 itr->getSource()->SendResetFailedNotify(GetId());
2422 else
2424 if(method == INSTANCE_RESET_GLOBAL)
2426 // set the homebind timer for players inside (1 minute)
2427 for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2428 itr->getSource()->m_InstanceValid = false;
2431 // the unload timer is not started
2432 // instead the map will unload immediately after the players have left
2433 m_unloadWhenEmpty = true;
2434 m_resetAfterUnload = true;
2437 else
2439 // unloaded at next update
2440 m_unloadTimer = MIN_UNLOAD_DELAY;
2441 m_resetAfterUnload = true;
2444 return m_mapRefManager.isEmpty();
2447 void InstanceMap::PermBindAllPlayers(Player *player)
2449 if(!IsDungeon())
2450 return;
2452 InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
2453 if(!save)
2455 sLog.outError("Cannot bind players, no instance save available for map!");
2456 return;
2459 Group *group = player->GetGroup();
2460 // group members outside the instance group don't get bound
2461 for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2463 Player* plr = itr->getSource();
2464 // players inside an instance cannot be bound to other instances
2465 // some players may already be permanently bound, in this case nothing happens
2466 InstancePlayerBind *bind = plr->GetBoundInstance(save->GetMapId(), save->GetDifficulty());
2467 if(!bind || !bind->perm)
2469 plr->BindToInstance(save, true);
2470 WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
2471 data << uint32(0);
2472 plr->GetSession()->SendPacket(&data);
2475 // if the leader is not in the instance the group will not get a perm bind
2476 if(group && group->GetLeaderGUID() == plr->GetGUID())
2477 group->BindToInstance(save, true);
2481 void InstanceMap::UnloadAll(bool pForce)
2483 if(HavePlayers())
2485 sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!");
2486 for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2488 Player* plr = itr->getSource();
2489 plr->TeleportTo(plr->m_homebindMapId, plr->m_homebindX, plr->m_homebindY, plr->m_homebindZ, plr->GetOrientation());
2493 if(m_resetAfterUnload == true)
2494 objmgr.DeleteRespawnTimeForInstance(GetInstanceId());
2496 Map::UnloadAll(pForce);
2499 void InstanceMap::SendResetWarnings(uint32 timeLeft) const
2501 for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
2502 itr->getSource()->SendInstanceResetWarning(GetId(), timeLeft);
2505 void InstanceMap::SetResetSchedule(bool on)
2507 // only for normal instances
2508 // the reset time is only scheduled when there are no payers inside
2509 // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
2510 if(IsDungeon() && !HavePlayers() && !IsRaid() && !IsHeroic())
2512 InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
2513 if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId());
2514 else sInstanceSaveManager.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), GetInstanceId()));
2518 uint32 InstanceMap::GetMaxPlayers() const
2520 InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId());
2521 if(!iTemplate)
2522 return 0;
2523 return IsHeroic() ? iTemplate->maxPlayersHeroic : iTemplate->maxPlayers;
2526 /* ******* Battleground Instance Maps ******* */
2528 BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId)
2529 : Map(id, expiry, InstanceId, DIFFICULTY_NORMAL)
2533 BattleGroundMap::~BattleGroundMap()
2537 bool BattleGroundMap::CanEnter(Player * player)
2539 if(player->GetMapRef().getTarget() == this)
2541 sLog.outError("BGMap::CanEnter - player %u already in map!", player->GetGUIDLow());
2542 assert(false);
2543 return false;
2546 if(player->GetBattleGroundId() != GetInstanceId())
2547 return false;
2549 // player number limit is checked in bgmgr, no need to do it here
2551 return Map::CanEnter(player);
2554 bool BattleGroundMap::Add(Player * player)
2557 Guard guard(*this);
2558 if(!CanEnter(player))
2559 return false;
2560 // reset instance validity, battleground maps do not homebind
2561 player->m_InstanceValid = true;
2563 return Map::Add(player);
2566 void BattleGroundMap::Remove(Player *player, bool remove)
2568 sLog.outDetail("MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
2569 Map::Remove(player, remove);
2572 void BattleGroundMap::SetUnload()
2574 m_unloadTimer = MIN_UNLOAD_DELAY;
2577 void BattleGroundMap::UnloadAll(bool pForce)
2579 while(HavePlayers())
2581 if(Player * plr = m_mapRefManager.getFirst()->getSource())
2583 plr->TeleportTo(plr->GetBattleGroundEntryPoint());
2584 // TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator.
2585 // just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop
2586 // note that this remove is not needed if the code works well in other places
2587 plr->GetMapRef().unlink();
2591 Map::UnloadAll(pForce);
2594 Creature*
2595 Map::GetCreature(uint64 guid)
2597 Creature * ret = ObjectAccessor::GetObjectInWorld(guid, (Creature*)NULL);
2598 if(!ret)
2599 return NULL;
2601 if(ret->GetMapId() != GetId())
2602 return NULL;
2604 if(ret->GetInstanceId() != GetInstanceId())
2605 return NULL;
2607 return ret;
2610 GameObject*
2611 Map::GetGameObject(uint64 guid)
2613 GameObject * ret = ObjectAccessor::GetObjectInWorld(guid, (GameObject*)NULL);
2614 if(!ret)
2615 return NULL;
2616 if(ret->GetMapId() != GetId())
2617 return NULL;
2618 if(ret->GetInstanceId() != GetInstanceId())
2619 return NULL;
2620 return ret;
2623 DynamicObject*
2624 Map::GetDynamicObject(uint64 guid)
2626 DynamicObject * ret = ObjectAccessor::GetObjectInWorld(guid, (DynamicObject*)NULL);
2627 if(!ret)
2628 return NULL;
2629 if(ret->GetMapId() != GetId())
2630 return NULL;
2631 if(ret->GetInstanceId() != GetInstanceId())
2632 return NULL;
2633 return ret;