| 1 | /* |
|---|
| 2 | * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> |
|---|
| 3 | * |
|---|
| 4 | * Thanks to the original authors: MaNGOS <http://www.mangosproject.org/> |
|---|
| 5 | * |
|---|
| 6 | * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | * it under the terms of the GNU General Public License as published by |
|---|
| 8 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 9 | * (at your option) any later version. |
|---|
| 10 | * |
|---|
| 11 | * This program is distributed in the hope that it will be useful, |
|---|
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | * GNU General Public License for more details. |
|---|
| 15 | * |
|---|
| 16 | * You should have received a copy of the GNU General Public License |
|---|
| 17 | * along with this program; if not, write to the Free Software |
|---|
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 19 | */ |
|---|
| 20 | |
|---|
| 21 | #include "MapInstanced.h" |
|---|
| 22 | #include "ObjectMgr.h" |
|---|
| 23 | #include "MapManager.h" |
|---|
| 24 | #include "BattleGround.h" |
|---|
| 25 | #include "VMapFactory.h" |
|---|
| 26 | #include "InstanceSaveMgr.h" |
|---|
| 27 | #include "World.h" |
|---|
| 28 | |
|---|
| 29 | MapInstanced::MapInstanced(uint32 id, time_t expiry, uint32 aInstanceId) : Map(id, expiry, 0, 0) |
|---|
| 30 | { |
|---|
| 31 | // initialize instanced maps list |
|---|
| 32 | m_InstancedMaps.clear(); |
|---|
| 33 | // fill with zero |
|---|
| 34 | memset(&GridMapReference, 0, MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_GRIDS*sizeof(uint16)); |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | void MapInstanced::Update(const uint32& t) |
|---|
| 38 | { |
|---|
| 39 | // take care of loaded GridMaps (when unused, unload it!) |
|---|
| 40 | Map::Update(t); |
|---|
| 41 | |
|---|
| 42 | // update the instanced maps |
|---|
| 43 | InstancedMaps::iterator i = m_InstancedMaps.begin(); |
|---|
| 44 | |
|---|
| 45 | while (i != m_InstancedMaps.end()) |
|---|
| 46 | { |
|---|
| 47 | if(i->second->CanUnload(t)) |
|---|
| 48 | { |
|---|
| 49 | DestroyInstance(i); // iterator incremented |
|---|
| 50 | } |
|---|
| 51 | else |
|---|
| 52 | { |
|---|
| 53 | // update only here, because it may schedule some bad things before delete |
|---|
| 54 | i->second->Update(t); |
|---|
| 55 | ++i; |
|---|
| 56 | } |
|---|
| 57 | } |
|---|
| 58 | } |
|---|
| 59 | |
|---|
| 60 | void MapInstanced::MoveAllCreaturesInMoveList() |
|---|
| 61 | { |
|---|
| 62 | for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) |
|---|
| 63 | { |
|---|
| 64 | i->second->MoveAllCreaturesInMoveList(); |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | Map::MoveAllCreaturesInMoveList(); |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | void MapInstanced::RemoveAllObjectsInRemoveList() |
|---|
| 71 | { |
|---|
| 72 | for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) |
|---|
| 73 | { |
|---|
| 74 | i->second->RemoveAllObjectsInRemoveList(); |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | Map::RemoveAllObjectsInRemoveList(); |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | bool MapInstanced::RemoveBones(uint64 guid, float x, float y) |
|---|
| 81 | { |
|---|
| 82 | bool remove_result = false; |
|---|
| 83 | |
|---|
| 84 | for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) |
|---|
| 85 | { |
|---|
| 86 | remove_result = remove_result || i->second->RemoveBones(guid, x, y); |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | return remove_result || Map::RemoveBones(guid,x,y); |
|---|
| 90 | } |
|---|
| 91 | |
|---|
| 92 | void MapInstanced::UnloadAll(bool pForce) |
|---|
| 93 | { |
|---|
| 94 | // Unload instanced maps |
|---|
| 95 | for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) |
|---|
| 96 | i->second->UnloadAll(pForce); |
|---|
| 97 | |
|---|
| 98 | // Delete the maps only after everything is unloaded to prevent crashes |
|---|
| 99 | for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) |
|---|
| 100 | delete i->second; |
|---|
| 101 | |
|---|
| 102 | m_InstancedMaps.clear(); |
|---|
| 103 | |
|---|
| 104 | // Unload own grids (just dummy(placeholder) grids, neccesary to unload GridMaps!) |
|---|
| 105 | Map::UnloadAll(pForce); |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | /* |
|---|
| 109 | - return the right instance for the object, based on its InstanceId |
|---|
| 110 | - create the instance if it's not created already |
|---|
| 111 | - the player is not actually added to the instance (only in InstanceMap::Add) |
|---|
| 112 | */ |
|---|
| 113 | Map* MapInstanced::GetInstance(const WorldObject* obj) |
|---|
| 114 | { |
|---|
| 115 | uint32 CurInstanceId = obj->GetInstanceId(); |
|---|
| 116 | Map* map = NULL; |
|---|
| 117 | |
|---|
| 118 | if (obj->GetMapId() == GetId() && CurInstanceId != 0) |
|---|
| 119 | { |
|---|
| 120 | // the object wants to be put in a certain instance of this map |
|---|
| 121 | map = _FindMap(CurInstanceId); |
|---|
| 122 | if(!map) |
|---|
| 123 | { |
|---|
| 124 | // For players if the instanceId is set, it's assumed they are already in a map, |
|---|
| 125 | // hence the map must be loaded. For Creatures, GameObjects etc the map must exist |
|---|
| 126 | // prior to calling GetMap, they are not allowed to create maps for themselves. |
|---|
| 127 | sLog.outError("GetInstance: object %s(%d), typeId %d, in world %d, should be in map %d,%d but that's not loaded yet.", obj->GetName(), obj->GetGUIDLow(), obj->GetTypeId(), obj->IsInWorld(), obj->GetMapId(), obj->GetInstanceId()); |
|---|
| 128 | assert(false); |
|---|
| 129 | } |
|---|
| 130 | return(map); |
|---|
| 131 | } |
|---|
| 132 | else |
|---|
| 133 | { |
|---|
| 134 | // instance not specified, find an existing or create a new one |
|---|
| 135 | if(obj->GetTypeId() != TYPEID_PLAYER) |
|---|
| 136 | { |
|---|
| 137 | sLog.outError("MAPINSTANCED: WorldObject '%u' (Entry: %u TypeID: %u) is in map %d,%d and requested base map instance of map %d, this must not happen", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), obj->GetMapId(), obj->GetInstanceId(), GetId()); |
|---|
| 138 | assert(false); |
|---|
| 139 | return NULL; |
|---|
| 140 | } |
|---|
| 141 | else |
|---|
| 142 | { |
|---|
| 143 | uint32 NewInstanceId = 0; // instanceId of the resulting map |
|---|
| 144 | Player* player = (Player*)obj; |
|---|
| 145 | |
|---|
| 146 | if(IsBattleGroundOrArena()) |
|---|
| 147 | { |
|---|
| 148 | // instantiate or find existing bg map for player |
|---|
| 149 | // the instance id is set in battlegroundid |
|---|
| 150 | NewInstanceId = player->GetBattleGroundId(); |
|---|
| 151 | assert(NewInstanceId); |
|---|
| 152 | map = _FindMap(NewInstanceId); |
|---|
| 153 | if(!map) |
|---|
| 154 | map = CreateBattleGround(NewInstanceId); |
|---|
| 155 | return map; |
|---|
| 156 | } |
|---|
| 157 | |
|---|
| 158 | InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty()); |
|---|
| 159 | InstanceSave *pSave = pBind ? pBind->save : NULL; |
|---|
| 160 | |
|---|
| 161 | // the player's permanet player bind is taken into consideration first |
|---|
| 162 | // then the player's group bind and finally the solo bind. |
|---|
| 163 | if(!pBind || !pBind->perm) |
|---|
| 164 | { |
|---|
| 165 | InstanceGroupBind *groupBind = NULL; |
|---|
| 166 | Group *group = player->GetGroup(); |
|---|
| 167 | // use the player's difficulty setting (it may not be the same as the group's) |
|---|
| 168 | if(group && (groupBind = group->GetBoundInstance(GetId(), player->GetDifficulty()))) |
|---|
| 169 | pSave = groupBind->save; |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | if(pSave) |
|---|
| 173 | { |
|---|
| 174 | // solo/perm/group |
|---|
| 175 | NewInstanceId = pSave->GetInstanceId(); |
|---|
| 176 | map = _FindMap(NewInstanceId); |
|---|
| 177 | // it is possible that the save exists but the map doesn't |
|---|
| 178 | if(!map) |
|---|
| 179 | map = CreateInstance(NewInstanceId, pSave, pSave->GetDifficulty()); |
|---|
| 180 | return map; |
|---|
| 181 | } |
|---|
| 182 | else |
|---|
| 183 | { |
|---|
| 184 | // if no instanceId via group members or instance saves is found |
|---|
| 185 | // the instance will be created for the first time |
|---|
| 186 | NewInstanceId = MapManager::Instance().GenerateInstanceId(); |
|---|
| 187 | return CreateInstance(NewInstanceId, NULL, player->GetDifficulty()); |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save, uint8 difficulty) |
|---|
| 194 | { |
|---|
| 195 | // load/create a map |
|---|
| 196 | Guard guard(*this); |
|---|
| 197 | |
|---|
| 198 | // make sure we have a valid map id |
|---|
| 199 | const MapEntry* entry = sMapStore.LookupEntry(GetId()); |
|---|
| 200 | if(!entry) |
|---|
| 201 | { |
|---|
| 202 | sLog.outError("CreateInstance: no entry for map %d", GetId()); |
|---|
| 203 | assert(false); |
|---|
| 204 | } |
|---|
| 205 | const InstanceTemplate * iTemplate = objmgr.GetInstanceTemplate(GetId()); |
|---|
| 206 | if(!iTemplate) |
|---|
| 207 | { |
|---|
| 208 | sLog.outError("CreateInstance: no instance template for map %d", GetId()); |
|---|
| 209 | assert(false); |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | // some instances only have one difficulty |
|---|
| 213 | if(!entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL; |
|---|
| 214 | |
|---|
| 215 | sLog.outDebug("MapInstanced::CreateInstance: %smap instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); |
|---|
| 216 | |
|---|
| 217 | InstanceMap *map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty); |
|---|
| 218 | assert(map->IsDungeon()); |
|---|
| 219 | |
|---|
| 220 | bool load_data = save != NULL; |
|---|
| 221 | map->CreateInstanceData(load_data); |
|---|
| 222 | |
|---|
| 223 | m_InstancedMaps[InstanceId] = map; |
|---|
| 224 | return map; |
|---|
| 225 | } |
|---|
| 226 | |
|---|
| 227 | BattleGroundMap* MapInstanced::CreateBattleGround(uint32 InstanceId) |
|---|
| 228 | { |
|---|
| 229 | // load/create a map |
|---|
| 230 | Guard guard(*this); |
|---|
| 231 | |
|---|
| 232 | sLog.outDebug("MapInstanced::CreateBattleGround: map bg %d for %d created.", InstanceId, GetId()); |
|---|
| 233 | |
|---|
| 234 | BattleGroundMap *map = new BattleGroundMap(GetId(), GetGridExpiry(), InstanceId); |
|---|
| 235 | assert(map->IsBattleGroundOrArena()); |
|---|
| 236 | |
|---|
| 237 | m_InstancedMaps[InstanceId] = map; |
|---|
| 238 | return map; |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | void MapInstanced::DestroyInstance(uint32 InstanceId) |
|---|
| 242 | { |
|---|
| 243 | InstancedMaps::iterator itr = m_InstancedMaps.find(InstanceId); |
|---|
| 244 | if(itr != m_InstancedMaps.end()) |
|---|
| 245 | DestroyInstance(itr); |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | // increments the iterator after erase |
|---|
| 249 | void MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) |
|---|
| 250 | { |
|---|
| 251 | itr->second->UnloadAll(true); |
|---|
| 252 | // should only unload VMaps if this is the last instance and grid unloading is enabled |
|---|
| 253 | if(m_InstancedMaps.size() <= 1 && sWorld.getConfig(CONFIG_GRID_UNLOAD)) |
|---|
| 254 | { |
|---|
| 255 | VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId()); |
|---|
| 256 | // in that case, unload grids of the base map, too |
|---|
| 257 | // so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded |
|---|
| 258 | Map::UnloadAll(true); |
|---|
| 259 | } |
|---|
| 260 | // erase map |
|---|
| 261 | delete itr->second; |
|---|
| 262 | m_InstancedMaps.erase(itr++); |
|---|
| 263 | } |
|---|
| 264 | |
|---|