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 "InstanceSaveMgr.h"
21 #include "Policies/SingletonImp.h"
22 #include "Database/DatabaseEnv.h"
24 #include "Transports.h"
25 #include "GridDefines.h"
26 #include "MapInstanced.h"
27 #include "DestinationHolderImp.h"
31 #include "ObjectMgr.h"
33 #define CLASS_LOCK MaNGOS::ClassLevelLockable<MapManager, ACE_Thread_Mutex>
34 INSTANTIATE_SINGLETON_2(MapManager
, CLASS_LOCK
);
35 INSTANTIATE_CLASS_MUTEX(MapManager
, ACE_Thread_Mutex
);
37 extern GridState
* si_GridStates
[]; // debugging code, should be deleted some day
39 MapManager::MapManager() : i_gridCleanUpDelay(sWorld
.getConfig(CONFIG_INTERVAL_GRIDCLEAN
))
41 i_timer
.SetInterval(sWorld
.getConfig(CONFIG_INTERVAL_MAPUPDATE
));
44 MapManager::~MapManager()
46 for(MapMapType::iterator iter
=i_maps
.begin(); iter
!= i_maps
.end(); ++iter
)
49 for(TransportSet::iterator i
= m_Transports
.begin(); i
!= m_Transports
.end(); ++i
)
52 Map::DeleteStateMachine();
56 MapManager::Initialize()
58 Map::InitStateMachine();
60 // debugging code, should be deleted some day
62 for(int i
=0;i
<MAX_GRID_STATE
; i
++)
64 i_GridStates
[i
] = si_GridStates
[i
];
66 i_GridStateErrorCount
= 0;
72 void MapManager::InitializeVisibilityDistanceInfo()
74 for(MapMapType::iterator iter
=i_maps
.begin(); iter
!= i_maps
.end(); ++iter
)
75 (*iter
).second
->InitVisibilityDistance();
78 // debugging code, should be deleted some day
79 void MapManager::checkAndCorrectGridStatesArray()
82 for(int i
=0;i
<MAX_GRID_STATE
; i
++)
84 if(i_GridStates
[i
] != si_GridStates
[i
])
86 sLog
.outError("MapManager::checkGridStates(), GridState: si_GridStates is currupt !!!");
88 si_GridStates
[i
] = i_GridStates
[i
];
91 // inner class checking only when compiled with debug
92 if(!si_GridStates
[i
]->checkMagic())
95 si_GridStates
[i
]->setMagic();
100 ++i_GridStateErrorCount
;
101 if(i_GridStateErrorCount
> 2)
102 assert(false); // force a crash. Too many errors
106 MapManager::_createBaseMap(uint32 id
)
108 Map
*m
= _findMap(id
);
114 const MapEntry
* entry
= sMapStore
.LookupEntry(id
);
115 if (entry
&& entry
->Instanceable())
117 m
= new MapInstanced(id
, i_gridCleanUpDelay
);
121 m
= new Map(id
, i_gridCleanUpDelay
, 0, 0);
130 Map
* MapManager::CreateMap(uint32 id
, const WorldObject
* obj
)
133 //if(!obj->IsInWorld()) sLog.outError("GetMap: called for map %d with object (typeid %d, guid %d, mapid %d, instanceid %d) who is not in world!", id, obj->GetTypeId(), obj->GetGUIDLow(), obj->GetMapId(), obj->GetInstanceId());
134 Map
*m
= _createBaseMap(id
);
136 if (m
&& (obj
->GetTypeId() == TYPEID_PLAYER
) && m
->Instanceable()) m
= ((MapInstanced
*)m
)->CreateInstance(id
, (Player
*)obj
);
141 Map
* MapManager::FindMap(uint32 mapid
, uint32 instanceId
) const
143 Map
*map
= _findMap(mapid
);
147 if(!map
->Instanceable())
148 return instanceId
== 0 ? map
: NULL
;
150 return ((MapInstanced
*)map
)->FindMap(instanceId
);
154 checks that do not require a map to be created
155 will send transfer error messages on fail
157 bool MapManager::CanPlayerEnter(uint32 mapid
, Player
* player
)
159 const MapEntry
*entry
= sMapStore
.LookupEntry(mapid
);
160 if(!entry
) return false;
161 const char *mapName
= entry
->name
[player
->GetSession()->GetSessionDbcLocale()];
163 if(entry
->map_type
== MAP_INSTANCE
|| entry
->map_type
== MAP_RAID
)
165 if (entry
->map_type
== MAP_RAID
)
167 // GMs can avoid raid limitations
168 if(!player
->isGameMaster() && !sWorld
.getConfig(CONFIG_INSTANCE_IGNORE_RAID
))
170 // can only enter in a raid group
171 Group
* group
= player
->GetGroup();
172 if (!group
|| !group
->isRaidGroup())
174 // probably there must be special opcode, because client has this string constant in GlobalStrings.lua
175 // TODO: this is not a good place to send the message
176 player
->GetSession()->SendAreaTriggerMessage("You must be in a raid group to enter %s instance", mapName
);
177 sLog
.outDebug("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player
->GetName(), mapName
);
183 //The player has a heroic mode and tries to enter into instance which has no a heroic mode
184 MapDifficulty
const* mapDiff
= GetMapDifficultyData(entry
->MapID
,player
->GetDifficulty(entry
->map_type
== MAP_RAID
));
187 bool isHeroicTargetMap
= entry
->map_type
== MAP_RAID
188 ? (player
->GetRaidDifficulty() >= RAID_DIFFICULTY_10MAN_HEROIC
)
189 : (player
->GetDungeonDifficulty() >= DUNGEON_DIFFICULTY_HEROIC
);
191 //Send aborted message
192 // FIX ME: what about absent normal/heroic mode with specific players limit...
193 player
->SendTransferAborted(mapid
, TRANSFER_ABORT_DIFFICULTY
, isHeroicTargetMap
? DUNGEON_DIFFICULTY_HEROIC
: DUNGEON_DIFFICULTY_NORMAL
);
197 if (!player
->isAlive())
199 if(Corpse
*corpse
= player
->GetCorpse())
201 // let enter in ghost mode in instance that connected to inner instance with corpse
202 uint32 instance_map
= corpse
->GetMapId();
205 if(instance_map
==mapid
)
208 InstanceTemplate
const* instance
= objmgr
.GetInstanceTemplate(instance_map
);
209 instance_map
= instance
? instance
->parent
: 0;
211 while (instance_map
);
215 player
->GetSession()->SendAreaTriggerMessage("You cannot enter %s while in a ghost mode", mapName
);
216 sLog
.outDebug("MAP: Player '%s' doesn't has a corpse in instance '%s' and can't enter", player
->GetName(), mapName
);
219 sLog
.outDebug("MAP: Player '%s' has corpse in instance '%s' and can enter", player
->GetName(), mapName
);
223 sLog
.outDebug("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player
->GetName());
227 // TODO: move this to a map dependent location
228 /*if(i_data && i_data->IsEncounterInProgress())
230 sLog.outDebug("MAP: Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName());
231 player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
240 void MapManager::DeleteInstance(uint32 mapid
, uint32 instanceId
)
242 Map
*m
= _createBaseMap(mapid
);
243 if (m
&& m
->Instanceable())
244 ((MapInstanced
*)m
)->DestroyInstance(instanceId
);
247 void MapManager::RemoveBonesFromMap(uint32 mapid
, uint64 guid
, float x
, float y
)
249 bool remove_result
= _createBaseMap(mapid
)->RemoveBones(guid
, x
, y
);
253 sLog
.outDebug("Bones %u not found in world. Delete from DB also.", GUID_LOPART(guid
));
258 MapManager::Update(uint32 diff
)
260 i_timer
.Update(diff
);
261 if( !i_timer
.Passed() )
264 for(MapMapType::iterator iter
=i_maps
.begin(); iter
!= i_maps
.end(); ++iter
)
266 checkAndCorrectGridStatesArray(); // debugging code, should be deleted some day
267 iter
->second
->Update(i_timer
.GetCurrent());
270 for (TransportSet::iterator iter
= m_Transports
.begin(); iter
!= m_Transports
.end(); ++iter
)
271 (*iter
)->Update(i_timer
.GetCurrent());
273 i_timer
.SetCurrent(0);
276 void MapManager::DoDelayedMovesAndRemoves()
278 for(MapMapType::iterator iter
=i_maps
.begin(); iter
!= i_maps
.end(); ++iter
)
279 iter
->second
->DoDelayedMovesAndRemoves();
282 bool MapManager::ExistMapAndVMap(uint32 mapid
, float x
,float y
)
284 GridPair p
= MaNGOS::ComputeGridPair(x
,y
);
289 return Map::ExistMap(mapid
,gx
,gy
) && Map::ExistVMap(mapid
,gx
,gy
);
292 bool MapManager::IsValidMAP(uint32 mapid
)
294 MapEntry
const* mEntry
= sMapStore
.LookupEntry(mapid
);
295 return mEntry
&& (!mEntry
->IsDungeon() || objmgr
.GetInstanceTemplate(mapid
));
296 // TODO: add check for battleground template
299 void MapManager::UnloadAll()
301 for(MapMapType::iterator iter
=i_maps
.begin(); iter
!= i_maps
.end(); ++iter
)
302 iter
->second
->UnloadAll(true);
304 while(!i_maps
.empty())
306 delete i_maps
.begin()->second
;
307 i_maps
.erase(i_maps
.begin());
311 void MapManager::InitMaxInstanceId()
315 QueryResult
*result
= CharacterDatabase
.Query( "SELECT MAX(id) FROM instance" );
318 i_MaxInstanceId
= result
->Fetch()[0].GetUInt32();
323 uint32
MapManager::GetNumInstances()
326 for(MapMapType::iterator itr
= i_maps
.begin(); itr
!= i_maps
.end(); ++itr
)
328 Map
*map
= itr
->second
;
329 if(!map
->Instanceable()) continue;
330 MapInstanced::InstancedMaps
&maps
= ((MapInstanced
*)map
)->GetInstancedMaps();
331 for(MapInstanced::InstancedMaps::iterator mitr
= maps
.begin(); mitr
!= maps
.end(); ++mitr
)
332 if(mitr
->second
->IsDungeon()) ret
++;
337 uint32
MapManager::GetNumPlayersInInstances()
340 for(MapMapType::iterator itr
= i_maps
.begin(); itr
!= i_maps
.end(); ++itr
)
342 Map
*map
= itr
->second
;
343 if(!map
->Instanceable()) continue;
344 MapInstanced::InstancedMaps
&maps
= ((MapInstanced
*)map
)->GetInstancedMaps();
345 for(MapInstanced::InstancedMaps::iterator mitr
= maps
.begin(); mitr
!= maps
.end(); ++mitr
)
346 if(mitr
->second
->IsDungeon())
347 ret
+= ((InstanceMap
*)mitr
->second
)->GetPlayers().getSize();