mirror of
https://github.com/mangosfour/server.git
synced 2025-12-31 04:37:04 +00:00
* Drop delayed moves list in Map code * Apply movement coords update always at call including movement to different cell/grid. * Instead removed functionality mark creature as need move notify broadcast at next tick, do it. This must resolve porblesm with CreatureRelocation in past not always update position to new expected at call And in resul next code fail or work in strange way. Mark creature for notifier call at next Update let safe main part remopved functionality implemented in another way: prevent cascade (or infinity chain) in move updates. In fiture possible implement move notify call not at each tick for save time.
348 lines
11 KiB
C++
348 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "MapManager.h"
|
|
#include "InstanceSaveMgr.h"
|
|
#include "Policies/SingletonImp.h"
|
|
#include "Database/DatabaseEnv.h"
|
|
#include "Log.h"
|
|
#include "Transports.h"
|
|
#include "GridDefines.h"
|
|
#include "MapInstanced.h"
|
|
#include "DestinationHolderImp.h"
|
|
#include "World.h"
|
|
#include "CellImpl.h"
|
|
#include "Corpse.h"
|
|
#include "ObjectMgr.h"
|
|
|
|
#define CLASS_LOCK MaNGOS::ClassLevelLockable<MapManager, ACE_Thread_Mutex>
|
|
INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK);
|
|
INSTANTIATE_CLASS_MUTEX(MapManager, ACE_Thread_Mutex);
|
|
|
|
extern GridState* si_GridStates[]; // debugging code, should be deleted some day
|
|
|
|
MapManager::MapManager() : i_gridCleanUpDelay(sWorld.getConfig(CONFIG_UINT32_INTERVAL_GRIDCLEAN))
|
|
{
|
|
i_timer.SetInterval(sWorld.getConfig(CONFIG_UINT32_INTERVAL_MAPUPDATE));
|
|
}
|
|
|
|
MapManager::~MapManager()
|
|
{
|
|
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
|
|
delete iter->second;
|
|
|
|
for(TransportSet::iterator i = m_Transports.begin(); i != m_Transports.end(); ++i)
|
|
delete *i;
|
|
|
|
Map::DeleteStateMachine();
|
|
}
|
|
|
|
void
|
|
MapManager::Initialize()
|
|
{
|
|
Map::InitStateMachine();
|
|
|
|
// debugging code, should be deleted some day
|
|
{
|
|
for(int i=0;i<MAX_GRID_STATE; i++)
|
|
{
|
|
i_GridStates[i] = si_GridStates[i];
|
|
}
|
|
i_GridStateErrorCount = 0;
|
|
}
|
|
|
|
InitMaxInstanceId();
|
|
}
|
|
|
|
void MapManager::InitializeVisibilityDistanceInfo()
|
|
{
|
|
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
|
|
(*iter).second->InitVisibilityDistance();
|
|
}
|
|
|
|
// debugging code, should be deleted some day
|
|
void MapManager::checkAndCorrectGridStatesArray()
|
|
{
|
|
bool ok = true;
|
|
for(int i=0;i<MAX_GRID_STATE; i++)
|
|
{
|
|
if(i_GridStates[i] != si_GridStates[i])
|
|
{
|
|
sLog.outError("MapManager::checkGridStates(), GridState: si_GridStates is currupt !!!");
|
|
ok = false;
|
|
si_GridStates[i] = i_GridStates[i];
|
|
}
|
|
#ifdef MANGOS_DEBUG
|
|
// inner class checking only when compiled with debug
|
|
if(!si_GridStates[i]->checkMagic())
|
|
{
|
|
ok = false;
|
|
si_GridStates[i]->setMagic();
|
|
}
|
|
#endif
|
|
}
|
|
if(!ok)
|
|
++i_GridStateErrorCount;
|
|
if(i_GridStateErrorCount > 2)
|
|
assert(false); // force a crash. Too many errors
|
|
}
|
|
|
|
Map*
|
|
MapManager::_createBaseMap(uint32 id)
|
|
{
|
|
Map *m = _findMap(id);
|
|
|
|
if( m == NULL )
|
|
{
|
|
Guard guard(*this);
|
|
|
|
const MapEntry* entry = sMapStore.LookupEntry(id);
|
|
if (entry && entry->Instanceable())
|
|
{
|
|
m = new MapInstanced(id, i_gridCleanUpDelay);
|
|
}
|
|
else
|
|
{
|
|
m = new Map(id, i_gridCleanUpDelay, 0, REGULAR_DIFFICULTY);
|
|
}
|
|
i_maps[id] = m;
|
|
}
|
|
|
|
assert(m != NULL);
|
|
return m;
|
|
}
|
|
|
|
Map* MapManager::CreateMap(uint32 id, const WorldObject* obj)
|
|
{
|
|
ASSERT(obj);
|
|
//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());
|
|
Map *m = _createBaseMap(id);
|
|
|
|
if (m && (obj->GetTypeId() == TYPEID_PLAYER) && m->Instanceable()) m = ((MapInstanced*)m)->CreateInstance(id, (Player*)obj);
|
|
|
|
return m;
|
|
}
|
|
|
|
Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const
|
|
{
|
|
Map *map = _findMap(mapid);
|
|
if(!map)
|
|
return NULL;
|
|
|
|
if(!map->Instanceable())
|
|
return instanceId == 0 ? map : NULL;
|
|
|
|
return ((MapInstanced*)map)->FindMap(instanceId);
|
|
}
|
|
|
|
/*
|
|
checks that do not require a map to be created
|
|
will send transfer error messages on fail
|
|
*/
|
|
bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
|
|
{
|
|
const MapEntry *entry = sMapStore.LookupEntry(mapid);
|
|
if(!entry) return false;
|
|
const char *mapName = entry->name[player->GetSession()->GetSessionDbcLocale()];
|
|
|
|
if(entry->map_type == MAP_INSTANCE || entry->map_type == MAP_RAID)
|
|
{
|
|
if (entry->map_type == MAP_RAID)
|
|
{
|
|
// GMs can avoid raid limitations
|
|
if(!player->isGameMaster() && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID))
|
|
{
|
|
// can only enter in a raid group
|
|
Group* group = player->GetGroup();
|
|
if (!group || !group->isRaidGroup())
|
|
{
|
|
// probably there must be special opcode, because client has this string constant in GlobalStrings.lua
|
|
// TODO: this is not a good place to send the message
|
|
player->GetSession()->SendAreaTriggerMessage("You must be in a raid group to enter %s instance", mapName);
|
|
sLog.outDebug("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player->GetName(), mapName);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//The player has a heroic mode and tries to enter into instance which has no a heroic mode
|
|
MapDifficulty const* mapDiff = GetMapDifficultyData(entry->MapID,player->GetDifficulty(entry->map_type == MAP_RAID));
|
|
if (!mapDiff)
|
|
{
|
|
bool isRegularTargetMap = player->GetDifficulty(entry->IsRaid()) == REGULAR_DIFFICULTY;
|
|
|
|
//Send aborted message
|
|
// FIX ME: what about absent normal/heroic mode with specific players limit...
|
|
player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, isRegularTargetMap ? DUNGEON_DIFFICULTY_NORMAL : DUNGEON_DIFFICULTY_HEROIC);
|
|
return false;
|
|
}
|
|
|
|
if (!player->isAlive())
|
|
{
|
|
if(Corpse *corpse = player->GetCorpse())
|
|
{
|
|
// let enter in ghost mode in instance that connected to inner instance with corpse
|
|
uint32 instance_map = corpse->GetMapId();
|
|
do
|
|
{
|
|
if(instance_map==mapid)
|
|
break;
|
|
|
|
InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(instance_map);
|
|
instance_map = instance ? instance->parent : 0;
|
|
}
|
|
while (instance_map);
|
|
|
|
if (!instance_map)
|
|
{
|
|
player->GetSession()->SendAreaTriggerMessage("You cannot enter %s while in a ghost mode", mapName);
|
|
sLog.outDebug("MAP: Player '%s' doesn't has a corpse in instance '%s' and can't enter", player->GetName(), mapName);
|
|
return false;
|
|
}
|
|
sLog.outDebug("MAP: Player '%s' has corpse in instance '%s' and can enter", player->GetName(), mapName);
|
|
}
|
|
else
|
|
{
|
|
sLog.outDebug("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player->GetName());
|
|
}
|
|
}
|
|
|
|
// TODO: move this to a map dependent location
|
|
/*if(i_data && i_data->IsEncounterInProgress())
|
|
{
|
|
sLog.outDebug("MAP: Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName());
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
|
|
return(false);
|
|
}*/
|
|
return true;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId)
|
|
{
|
|
Map *m = _createBaseMap(mapid);
|
|
if (m && m->Instanceable())
|
|
((MapInstanced*)m)->DestroyInstance(instanceId);
|
|
}
|
|
|
|
void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y)
|
|
{
|
|
bool remove_result = _createBaseMap(mapid)->RemoveBones(guid, x, y);
|
|
|
|
if (!remove_result)
|
|
{
|
|
sLog.outDebug("Bones %u not found in world. Delete from DB also.", GUID_LOPART(guid));
|
|
}
|
|
}
|
|
|
|
void
|
|
MapManager::Update(uint32 diff)
|
|
{
|
|
i_timer.Update(diff);
|
|
if( !i_timer.Passed() )
|
|
return;
|
|
|
|
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
|
|
{
|
|
checkAndCorrectGridStatesArray(); // debugging code, should be deleted some day
|
|
iter->second->Update((uint32)i_timer.GetCurrent());
|
|
}
|
|
|
|
for (TransportSet::iterator iter = m_Transports.begin(); iter != m_Transports.end(); ++iter)
|
|
(*iter)->Update(i_timer.GetCurrent());
|
|
|
|
i_timer.SetCurrent(0);
|
|
}
|
|
|
|
void MapManager::RemoveAllObjectsInRemoveList()
|
|
{
|
|
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
|
|
iter->second->RemoveAllObjectsInRemoveList();
|
|
}
|
|
|
|
bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y)
|
|
{
|
|
GridPair p = MaNGOS::ComputeGridPair(x,y);
|
|
|
|
int gx=63-p.x_coord;
|
|
int gy=63-p.y_coord;
|
|
|
|
return Map::ExistMap(mapid,gx,gy) && Map::ExistVMap(mapid,gx,gy);
|
|
}
|
|
|
|
bool MapManager::IsValidMAP(uint32 mapid)
|
|
{
|
|
MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
|
|
return mEntry && (!mEntry->IsDungeon() || ObjectMgr::GetInstanceTemplate(mapid));
|
|
// TODO: add check for battleground template
|
|
}
|
|
|
|
void MapManager::UnloadAll()
|
|
{
|
|
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
|
|
iter->second->UnloadAll(true);
|
|
|
|
while(!i_maps.empty())
|
|
{
|
|
delete i_maps.begin()->second;
|
|
i_maps.erase(i_maps.begin());
|
|
}
|
|
}
|
|
|
|
void MapManager::InitMaxInstanceId()
|
|
{
|
|
i_MaxInstanceId = 0;
|
|
|
|
QueryResult *result = CharacterDatabase.Query( "SELECT MAX(id) FROM instance" );
|
|
if( result )
|
|
{
|
|
i_MaxInstanceId = result->Fetch()[0].GetUInt32();
|
|
delete result;
|
|
}
|
|
}
|
|
|
|
uint32 MapManager::GetNumInstances()
|
|
{
|
|
uint32 ret = 0;
|
|
for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
|
|
{
|
|
Map *map = itr->second;
|
|
if(!map->Instanceable()) continue;
|
|
MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps();
|
|
for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
|
|
if(mitr->second->IsDungeon()) ret++;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
uint32 MapManager::GetNumPlayersInInstances()
|
|
{
|
|
uint32 ret = 0;
|
|
for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr)
|
|
{
|
|
Map *map = itr->second;
|
|
if(!map->Instanceable()) continue;
|
|
MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps();
|
|
for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr)
|
|
if(mitr->second->IsDungeon())
|
|
ret += ((InstanceMap*)mitr->second)->GetPlayers().getSize();
|
|
}
|
|
return ret;
|
|
}
|