mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-18 08:28:32 +01:00
Delete a repeated check in instance canenter(). Fix some spell targets. Add some sunwell spell sql. Fix Magtheridons earthquake. (TODO: need to find out why soul transfer has no effect when casted by mobs) Let Brutallus dual wield. Enable burn (still no script effect). Quick fix for shadowmoon valley illidan quest crash (wait for author's fix). --HG-- branch : trunk
1819 lines
63 KiB
C++
1819 lines
63 KiB
C++
/*
|
|
* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
|
|
*
|
|
* Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
|
|
*
|
|
* 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 "Player.h"
|
|
#include "GridNotifiers.h"
|
|
#include "WorldSession.h"
|
|
#include "Log.h"
|
|
#include "GridStates.h"
|
|
#include "CellImpl.h"
|
|
#include "InstanceData.h"
|
|
#include "Map.h"
|
|
#include "GridNotifiersImpl.h"
|
|
#include "Config/ConfigEnv.h"
|
|
#include "Transports.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "ObjectMgr.h"
|
|
#include "World.h"
|
|
#include "ScriptCalls.h"
|
|
#include "Group.h"
|
|
|
|
#include "MapInstanced.h"
|
|
#include "InstanceSaveMgr.h"
|
|
#include "VMapFactory.h"
|
|
|
|
#define DEFAULT_GRID_EXPIRY 300
|
|
#define MAX_GRID_LOAD_TIME 50
|
|
|
|
// magic *.map header
|
|
const char MAP_MAGIC[] = "MAP_2.00";
|
|
|
|
GridState* si_GridStates[MAX_GRID_STATE];
|
|
|
|
Map::~Map()
|
|
{
|
|
UnloadAll(true);
|
|
}
|
|
|
|
bool Map::ExistMap(uint32 mapid,int x,int y)
|
|
{
|
|
int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
|
|
char* tmp = new char[len];
|
|
snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y);
|
|
|
|
FILE *pf=fopen(tmp,"rb");
|
|
|
|
if(!pf)
|
|
{
|
|
sLog.outError("Check existing of map file '%s': not exist!",tmp);
|
|
delete[] tmp;
|
|
return false;
|
|
}
|
|
|
|
char magic[8];
|
|
fread(magic,1,8,pf);
|
|
if(strncmp(MAP_MAGIC,magic,8))
|
|
{
|
|
sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
|
|
delete [] tmp;
|
|
fclose(pf); //close file before return
|
|
return false;
|
|
}
|
|
|
|
delete [] tmp;
|
|
fclose(pf);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Map::ExistVMap(uint32 mapid,int x,int y)
|
|
{
|
|
if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
|
|
{
|
|
if(vmgr->isMapLoadingEnabled())
|
|
{
|
|
// x and y are swaped !! => fixed now
|
|
bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, x,y);
|
|
if(!exists)
|
|
{
|
|
std::string name = vmgr->getDirFileName(mapid,x,y);
|
|
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());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Map::LoadVMap(int x,int y)
|
|
{
|
|
// x and y are swaped !!
|
|
int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), x,y);
|
|
switch(vmapLoadResult)
|
|
{
|
|
case VMAP::VMAP_LOAD_RESULT_OK:
|
|
sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
|
|
break;
|
|
case VMAP::VMAP_LOAD_RESULT_ERROR:
|
|
sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
|
|
break;
|
|
case VMAP::VMAP_LOAD_RESULT_IGNORED:
|
|
DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Map::LoadMap(uint32 mapid, uint32 instanceid, int x,int y)
|
|
{
|
|
if( instanceid != 0 )
|
|
{
|
|
if(GridMaps[x][y])
|
|
return;
|
|
|
|
Map* baseMap = const_cast<Map*>(MapManager::Instance().GetBaseMap(mapid));
|
|
|
|
// load gridmap for base map
|
|
if (!baseMap->GridMaps[x][y])
|
|
baseMap->EnsureGridCreated(GridPair(63-x,63-y));
|
|
|
|
//+++ if (!baseMap->GridMaps[x][y]) don't check for GridMaps[gx][gy], we need the management for vmaps
|
|
// return;
|
|
|
|
((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(x,y));
|
|
baseMap->SetUnloadFlag(GridPair(63-x,63-y), false);
|
|
GridMaps[x][y] = baseMap->GridMaps[x][y];
|
|
return;
|
|
}
|
|
|
|
//map already load, delete it before reloading (Is it neccessary? Do we really need the abilty the reload maps during runtime?)
|
|
if(GridMaps[x][y])
|
|
{
|
|
sLog.outDetail("Unloading already loaded map %u before reloading.",mapid);
|
|
delete (GridMaps[x][y]);
|
|
GridMaps[x][y]=NULL;
|
|
}
|
|
|
|
// map file name
|
|
char *tmp=NULL;
|
|
// Pihhan: dataPath length + "maps/" + 3+2+2+ ".map" length may be > 32 !
|
|
int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
|
|
tmp = new char[len];
|
|
snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y);
|
|
sLog.outDetail("Loading map %s",tmp);
|
|
// loading data
|
|
FILE *pf=fopen(tmp,"rb");
|
|
if(!pf)
|
|
{
|
|
delete [] tmp;
|
|
return;
|
|
}
|
|
|
|
char magic[8];
|
|
fread(magic,1,8,pf);
|
|
if(strncmp(MAP_MAGIC,magic,8))
|
|
{
|
|
sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp);
|
|
delete [] tmp;
|
|
fclose(pf); //close file before return
|
|
return;
|
|
}
|
|
delete [] tmp;
|
|
|
|
GridMap * buf= new GridMap;
|
|
fread(buf,1,sizeof(GridMap),pf);
|
|
fclose(pf);
|
|
|
|
GridMaps[x][y] = buf;
|
|
}
|
|
|
|
void Map::LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x,int y)
|
|
{
|
|
LoadMap(mapid,instanceid,x,y);
|
|
if(instanceid == 0)
|
|
LoadVMap(x, y); // Only load the data for the base map
|
|
}
|
|
|
|
void Map::InitStateMachine()
|
|
{
|
|
si_GridStates[GRID_STATE_INVALID] = new InvalidState;
|
|
si_GridStates[GRID_STATE_ACTIVE] = new ActiveState;
|
|
si_GridStates[GRID_STATE_IDLE] = new IdleState;
|
|
si_GridStates[GRID_STATE_REMOVAL] = new RemovalState;
|
|
}
|
|
|
|
void Map::DeleteStateMachine()
|
|
{
|
|
delete si_GridStates[GRID_STATE_INVALID];
|
|
delete si_GridStates[GRID_STATE_ACTIVE];
|
|
delete si_GridStates[GRID_STATE_IDLE];
|
|
delete si_GridStates[GRID_STATE_REMOVAL];
|
|
}
|
|
|
|
Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
|
|
: i_id(id), i_gridExpiry(expiry), i_mapEntry (sMapStore.LookupEntry(id)),
|
|
i_InstanceId(InstanceId), i_spawnMode(SpawnMode), m_unloadTimer(0)
|
|
{
|
|
for(unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
|
|
{
|
|
for(unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j)
|
|
{
|
|
//z code
|
|
GridMaps[idx][j] =NULL;
|
|
setNGrid(NULL, idx, j);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Template specialization of utility methods
|
|
template<class T>
|
|
void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj, obj->GetGUID());
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToGrid(Player* obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToGrid(Corpse *obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
// add to world object registry in grid
|
|
if(obj->GetType()!=CORPSE_BONES)
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
|
|
}
|
|
// add to grid object store
|
|
else
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID());
|
|
}
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
// add to world object registry in grid
|
|
if(obj->isPet())
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).AddWorldObject<Creature>(obj, obj->GetGUID());
|
|
obj->SetCurrentCell(cell);
|
|
}
|
|
// add to grid object store
|
|
else
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).AddGridObject<Creature>(obj, obj->GetGUID());
|
|
obj->SetCurrentCell(cell);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).template RemoveGridObject<T>(obj, obj->GetGUID());
|
|
}
|
|
|
|
template<>
|
|
void Map::RemoveFromGrid(Player* obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
|
|
}
|
|
|
|
template<>
|
|
void Map::RemoveFromGrid(Corpse *obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
// remove from world object registry in grid
|
|
if(obj->GetType()!=CORPSE_BONES)
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID());
|
|
}
|
|
// remove from grid object store
|
|
else
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).RemoveGridObject(obj, obj->GetGUID());
|
|
}
|
|
}
|
|
|
|
template<>
|
|
void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell)
|
|
{
|
|
// remove from world object registry in grid
|
|
if(obj->isPet())
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject<Creature>(obj, obj->GetGUID());
|
|
}
|
|
// remove from grid object store
|
|
else
|
|
{
|
|
(*grid)(cell.CellX(), cell.CellY()).RemoveGridObject<Creature>(obj, obj->GetGUID());
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
void Map::DeleteFromWorld(T* obj)
|
|
{
|
|
// Note: In case resurrectable corpse and pet its removed from gloabal lists in own destructors
|
|
delete obj;
|
|
}
|
|
|
|
template<class T>
|
|
void Map::AddNotifier(T* , Cell const& , CellPair const& )
|
|
{
|
|
}
|
|
|
|
template<>
|
|
void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair)
|
|
{
|
|
PlayerRelocationNotify(obj,cell,cellpair);
|
|
}
|
|
|
|
template<>
|
|
void Map::AddNotifier(Creature* obj, Cell const& cell, CellPair const& cellpair)
|
|
{
|
|
CreatureRelocationNotify(obj,cell,cellpair);
|
|
}
|
|
|
|
void
|
|
Map::EnsureGridCreated(const GridPair &p)
|
|
{
|
|
if(!getNGrid(p.x_coord, p.y_coord))
|
|
{
|
|
Guard guard(*this);
|
|
if(!getNGrid(p.x_coord, p.y_coord))
|
|
{
|
|
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)),
|
|
p.x_coord, p.y_coord);
|
|
|
|
// build a linkage between this map and NGridType
|
|
buildNGridLinkage(getNGrid(p.x_coord, p.y_coord));
|
|
|
|
getNGrid(p.x_coord, p.y_coord)->SetGridState(GRID_STATE_IDLE);
|
|
|
|
//z coord
|
|
int gx=63-p.x_coord;
|
|
int gy=63-p.y_coord;
|
|
|
|
if(!GridMaps[gx][gy])
|
|
Map::LoadMapAndVMap(i_id,i_InstanceId,gx,gy);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Map::EnsureGridLoadedForPlayer(const Cell &cell, Player *player, bool add_player)
|
|
{
|
|
EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
|
|
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
|
|
|
|
assert(grid != NULL);
|
|
if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
|
|
{
|
|
if( player != NULL )
|
|
{
|
|
player->SendDelayResponse(MAX_GRID_LOAD_TIME);
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_LOG("Player nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id);
|
|
}
|
|
|
|
ObjectGridLoader loader(*grid, this, cell);
|
|
loader.LoadN();
|
|
setGridObjectDataLoaded(true, cell.GridX(), cell.GridY());
|
|
|
|
// Add resurrectable corpses to world object list in grid
|
|
ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
|
|
|
|
ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f);
|
|
grid->SetGridState(GRID_STATE_ACTIVE);
|
|
|
|
if( add_player && player != NULL )
|
|
(*grid)(cell.CellX(), cell.CellY()).AddWorldObject(player, player->GetGUID());
|
|
}
|
|
else if( player && add_player )
|
|
AddToGrid(player,grid,cell);
|
|
}
|
|
|
|
void
|
|
Map::LoadGrid(const Cell& cell, bool no_unload)
|
|
{
|
|
EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
|
|
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
|
|
|
|
assert(grid != NULL);
|
|
if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
|
|
{
|
|
ObjectGridLoader loader(*grid, this, cell);
|
|
loader.LoadN();
|
|
|
|
// Add resurrectable corpses to world object list in grid
|
|
ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this);
|
|
|
|
setGridObjectDataLoaded(true,cell.GridX(), cell.GridY());
|
|
if(no_unload)
|
|
getNGrid(cell.GridX(), cell.GridY())->setUnloadFlag(false);
|
|
}
|
|
LoadVMap(63-cell.GridX(),63-cell.GridY());
|
|
}
|
|
|
|
bool Map::Add(Player *player)
|
|
{
|
|
player->SetInstanceId(this->GetInstanceId());
|
|
|
|
// update player state for other player and visa-versa
|
|
CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
|
|
Cell cell(p);
|
|
EnsureGridLoadedForPlayer(cell, player, true);
|
|
player->AddToWorld();
|
|
|
|
SendInitSelf(player);
|
|
SendInitTransports(player);
|
|
|
|
UpdatePlayerVisibility(player,cell,p);
|
|
UpdateObjectsVisibilityFor(player,cell,p);
|
|
|
|
AddNotifier(player,cell,p);
|
|
return true;
|
|
}
|
|
|
|
template<class T>
|
|
void
|
|
Map::Add(T *obj)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
|
|
|
|
assert(obj);
|
|
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
|
|
{
|
|
sLog.outError("Map::Add: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
|
|
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
|
|
assert( grid != NULL );
|
|
|
|
AddToGrid(obj,grid,cell);
|
|
obj->AddToWorld();
|
|
|
|
DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY());
|
|
|
|
UpdateObjectVisibility(obj,cell,p);
|
|
|
|
AddNotifier(obj,cell,p);
|
|
}
|
|
|
|
void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
|
|
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
|
|
{
|
|
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);
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
|
|
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
|
|
return;
|
|
|
|
Trinity::MessageDeliverer post_man(*player, msg, to_self);
|
|
TypeContainerVisitor<Trinity::MessageDeliverer, WorldTypeMapContainer > message(post_man);
|
|
CellLock<ReadGuard> cell_lock(cell, p);
|
|
cell_lock->Visit(cell_lock, message, *this);
|
|
}
|
|
|
|
void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
|
|
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
|
|
{
|
|
sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
cell.SetNoCreate();
|
|
|
|
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
|
|
return;
|
|
|
|
Trinity::ObjectMessageDeliverer post_man(msg);
|
|
TypeContainerVisitor<Trinity::ObjectMessageDeliverer, WorldTypeMapContainer > message(post_man);
|
|
CellLock<ReadGuard> cell_lock(cell, p);
|
|
cell_lock->Visit(cell_lock, message, *this);
|
|
}
|
|
|
|
void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
|
|
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
|
|
{
|
|
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);
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
|
|
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
|
|
return;
|
|
|
|
Trinity::MessageDistDeliverer post_man(*player, msg, dist, to_self, own_team_only);
|
|
TypeContainerVisitor<Trinity::MessageDistDeliverer , WorldTypeMapContainer > message(post_man);
|
|
CellLock<ReadGuard> cell_lock(cell, p);
|
|
cell_lock->Visit(cell_lock, message, *this);
|
|
}
|
|
|
|
void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
|
|
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
|
|
{
|
|
sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
cell.SetNoCreate();
|
|
|
|
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
|
|
return;
|
|
|
|
Trinity::ObjectMessageDistDeliverer post_man(*obj, msg,dist);
|
|
TypeContainerVisitor<Trinity::ObjectMessageDistDeliverer, WorldTypeMapContainer > message(post_man);
|
|
CellLock<ReadGuard> cell_lock(cell, p);
|
|
cell_lock->Visit(cell_lock, message, *this);
|
|
}
|
|
|
|
bool Map::loaded(const GridPair &p) const
|
|
{
|
|
return ( getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord) );
|
|
}
|
|
|
|
void Map::Update(const uint32 &t_diff)
|
|
{
|
|
// 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 !
|
|
// This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended
|
|
if (IsBattleGroundOrArena())
|
|
return;
|
|
|
|
for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
|
|
{
|
|
NGridType *grid = i->getSource();
|
|
GridInfo *info = i->getSource()->getGridInfoRef();
|
|
++i; // The update might delete the map and we need the next map before the iterator gets invalid
|
|
assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE);
|
|
si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff);
|
|
}
|
|
}
|
|
|
|
void InstanceMap::Update(const uint32& t_diff)
|
|
{
|
|
Map::Update(t_diff);
|
|
|
|
if(i_data)
|
|
i_data->Update(t_diff);
|
|
}
|
|
|
|
|
|
void Map::Remove(Player *player, bool remove)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP)
|
|
{
|
|
// invalid coordinates
|
|
player->RemoveFromWorld();
|
|
|
|
if( remove )
|
|
DeleteFromWorld(player);
|
|
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
|
|
if( !getNGrid(cell.data.Part.grid_x, cell.data.Part.grid_y) )
|
|
{
|
|
sLog.outError("Map::Remove() i_grids was NULL x:%d, y:%d",cell.data.Part.grid_x,cell.data.Part.grid_y);
|
|
return;
|
|
}
|
|
|
|
DEBUG_LOG("Remove player %s from grid[%u,%u]", player->GetName(), cell.GridX(), cell.GridY());
|
|
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
|
|
assert(grid != NULL);
|
|
|
|
player->RemoveFromWorld();
|
|
RemoveFromGrid(player,grid,cell);
|
|
|
|
SendRemoveTransports(player);
|
|
|
|
UpdateObjectsVisibilityFor(player,cell,p);
|
|
|
|
if( remove )
|
|
DeleteFromWorld(player);
|
|
}
|
|
|
|
bool Map::RemoveBones(uint64 guid, float x, float y)
|
|
{
|
|
if (IsRemovalGrid(x, y))
|
|
{
|
|
Corpse * corpse = ObjectAccessor::Instance().GetObjectInWorld(GetId(), x, y, guid, (Corpse*)NULL);
|
|
if(corpse && corpse->GetTypeId() == TYPEID_CORPSE && corpse->GetType() == CORPSE_BONES)
|
|
corpse->DeleteBonesFromWorld();
|
|
else
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template<class T>
|
|
void
|
|
Map::Remove(T *obj, bool remove)
|
|
{
|
|
CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
|
|
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
|
|
{
|
|
sLog.outError("Map::Remove: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
|
|
return;
|
|
}
|
|
|
|
Cell cell(p);
|
|
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
|
|
return;
|
|
|
|
DEBUG_LOG("Remove object " I64FMTD " from grid[%u,%u]", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y);
|
|
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
|
|
assert( grid != NULL );
|
|
|
|
obj->RemoveFromWorld();
|
|
RemoveFromGrid(obj,grid,cell);
|
|
|
|
UpdateObjectVisibility(obj,cell,p);
|
|
|
|
if( remove )
|
|
{
|
|
// if option set then object already saved at this moment
|
|
if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
|
|
obj->SaveRespawnTime();
|
|
DeleteFromWorld(obj);
|
|
}
|
|
}
|
|
|
|
void
|
|
Map::PlayerRelocation(Player *player, float x, float y, float z, float orientation)
|
|
{
|
|
assert(player);
|
|
|
|
CellPair old_val = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
|
|
CellPair new_val = Trinity::ComputeCellPair(x, y);
|
|
|
|
Cell old_cell(old_val);
|
|
Cell new_cell(new_val);
|
|
new_cell |= old_cell;
|
|
bool same_cell = (new_cell == old_cell);
|
|
|
|
player->Relocate(x, y, z, orientation);
|
|
|
|
if( old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell) )
|
|
{
|
|
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());
|
|
|
|
// update player position for group at taxi flight
|
|
if(player->GetGroup() && player->isInFlight())
|
|
player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
|
|
|
|
NGridType* oldGrid = getNGrid(old_cell.GridX(), old_cell.GridY());
|
|
RemoveFromGrid(player, oldGrid,old_cell);
|
|
if( !old_cell.DiffGrid(new_cell) )
|
|
AddToGrid(player, oldGrid,new_cell);
|
|
|
|
if( old_cell.DiffGrid(new_cell) )
|
|
EnsureGridLoadedForPlayer(new_cell, player, true);
|
|
}
|
|
|
|
// if move then update what player see and who seen
|
|
UpdatePlayerVisibility(player,new_cell,new_val);
|
|
UpdateObjectsVisibilityFor(player,new_cell,new_val);
|
|
PlayerRelocationNotify(player,new_cell,new_val);
|
|
NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY());
|
|
if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE )
|
|
{
|
|
ResetGridExpiry(*newGrid, 0.1f);
|
|
newGrid->SetGridState(GRID_STATE_ACTIVE);
|
|
}
|
|
}
|
|
|
|
void
|
|
Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang)
|
|
{
|
|
assert(CheckGridIntegrity(creature,false));
|
|
|
|
Cell old_cell = creature->GetCurrentCell();
|
|
|
|
CellPair new_val = Trinity::ComputeCellPair(x, y);
|
|
Cell new_cell(new_val);
|
|
|
|
// delay creature move for grid/cell to grid/cell moves
|
|
if( old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell) )
|
|
{
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
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());
|
|
#endif
|
|
AddCreatureToMoveList(creature,x,y,z,ang);
|
|
// in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList
|
|
}
|
|
else
|
|
{
|
|
creature->Relocate(x, y, z, ang);
|
|
CreatureRelocationNotify(creature,new_cell,new_val);
|
|
}
|
|
assert(CheckGridIntegrity(creature,true));
|
|
}
|
|
|
|
void Map::AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang)
|
|
{
|
|
if(!c)
|
|
return;
|
|
|
|
i_creaturesToMove[c] = CreatureMover(x,y,z,ang);
|
|
}
|
|
|
|
void Map::MoveAllCreaturesInMoveList()
|
|
{
|
|
while(!i_creaturesToMove.empty())
|
|
{
|
|
// get data and remove element;
|
|
CreatureMoveList::iterator iter = i_creaturesToMove.begin();
|
|
Creature* c = iter->first;
|
|
CreatureMover cm = iter->second;
|
|
i_creaturesToMove.erase(iter);
|
|
|
|
// calculate cells
|
|
CellPair new_val = Trinity::ComputeCellPair(cm.x, cm.y);
|
|
Cell new_cell(new_val);
|
|
|
|
// do move or do move to respawn or remove creature if previous all fail
|
|
if(CreatureCellRelocation(c,new_cell))
|
|
{
|
|
// update pos
|
|
c->Relocate(cm.x, cm.y, cm.z, cm.ang);
|
|
CreatureRelocationNotify(c,new_cell,new_cell.cellPair());
|
|
}
|
|
else
|
|
{
|
|
// if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid
|
|
// creature coordinates will be updated and notifiers send
|
|
if(!CreatureRespawnRelocation(c))
|
|
{
|
|
// ... or unload (if respawn grid also not loaded)
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
sLog.outDebug("Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",c->GetGUIDLow(),c->GetEntry());
|
|
#endif
|
|
c->CleanupsBeforeDelete();
|
|
AddObjectToRemoveList(c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
|
|
{
|
|
Cell const& old_cell = c->GetCurrentCell();
|
|
if(!old_cell.DiffGrid(new_cell) ) // in same grid
|
|
{
|
|
// if in same cell then none do
|
|
if(old_cell.DiffCell(new_cell))
|
|
{
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
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());
|
|
#endif
|
|
|
|
if( !old_cell.DiffGrid(new_cell) )
|
|
{
|
|
RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
|
|
AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
|
|
c->SetCurrentCell(new_cell);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
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());
|
|
#endif
|
|
}
|
|
}
|
|
else // in diff. grids
|
|
if(loaded(GridPair(new_cell.GridX(), new_cell.GridY())))
|
|
{
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
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());
|
|
#endif
|
|
|
|
RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
|
|
{
|
|
EnsureGridCreated(GridPair(new_cell.GridX(), new_cell.GridY()));
|
|
AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
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());
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Map::CreatureRespawnRelocation(Creature *c)
|
|
{
|
|
float resp_x, resp_y, resp_z, resp_o;
|
|
c->GetRespawnCoord(resp_x, resp_y, resp_z, &resp_o);
|
|
|
|
CellPair resp_val = Trinity::ComputeCellPair(resp_x, resp_y);
|
|
Cell resp_cell(resp_val);
|
|
|
|
c->CombatStop();
|
|
c->GetMotionMaster()->Clear();
|
|
|
|
#ifdef TRINITY_DEBUG
|
|
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
|
|
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());
|
|
#endif
|
|
|
|
// teleport it to respawn point (like normal respawn if player see)
|
|
if(CreatureCellRelocation(c,resp_cell))
|
|
{
|
|
c->Relocate(resp_x, resp_y, resp_z, resp_o);
|
|
c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators
|
|
CreatureRelocationNotify(c,resp_cell,resp_cell.cellPair());
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool pForce)
|
|
{
|
|
NGridType *grid = getNGrid(x, y);
|
|
assert( grid != NULL);
|
|
|
|
{
|
|
if(!pForce && ObjectAccessor::Instance().ActiveObjectsNearGrid(x, y, i_id, i_InstanceId) )
|
|
return false;
|
|
|
|
DEBUG_LOG("Unloading grid[%u,%u] for map %u", x,y, i_id);
|
|
ObjectGridUnloader unloader(*grid);
|
|
|
|
// Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
|
|
// Must know real mob position before move
|
|
DoDelayedMovesAndRemoves();
|
|
|
|
// move creatures to respawn grids if this is diff.grid or to remove list
|
|
unloader.MoveToRespawnN();
|
|
|
|
// Finish creature moves, remove and delete all creatures with delayed remove before unload
|
|
DoDelayedMovesAndRemoves();
|
|
|
|
unloader.UnloadN();
|
|
delete getNGrid(x, y);
|
|
setNGrid(NULL, x, y);
|
|
}
|
|
int gx=63-x;
|
|
int gy=63-y;
|
|
|
|
// delete grid map, but don't delete if it is from parent map (and thus only reference)
|
|
//+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps
|
|
{
|
|
if (i_InstanceId == 0)
|
|
{
|
|
if(GridMaps[gx][gy]) delete (GridMaps[gx][gy]);
|
|
// x and y are swaped
|
|
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx);
|
|
}
|
|
else
|
|
((MapInstanced*)(MapManager::Instance().GetBaseMap(i_id)))->RemoveGridMapReference(GridPair(gx, gy));
|
|
GridMaps[gx][gy] = NULL;
|
|
}
|
|
DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, i_id);
|
|
return true;
|
|
}
|
|
|
|
void Map::UnloadAll(bool pForce)
|
|
{
|
|
// clear all delayed moves, useless anyway do this moves before map unload.
|
|
i_creaturesToMove.clear();
|
|
|
|
for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
|
|
{
|
|
NGridType &grid(*i->getSource());
|
|
++i;
|
|
UnloadGrid(grid.getX(), grid.getY(), pForce); // deletes the grid and removes it from the GridRefManager
|
|
}
|
|
}
|
|
|
|
float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
|
|
{
|
|
GridPair p = Trinity::ComputeGridPair(x, y);
|
|
|
|
// half opt method
|
|
int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x
|
|
int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
|
|
|
|
float lx=MAP_RESOLUTION*(32 -x/SIZE_OF_GRIDS - gx);
|
|
float ly=MAP_RESOLUTION*(32 -y/SIZE_OF_GRIDS - gy);
|
|
|
|
// ensure GridMap is loaded
|
|
const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
|
|
|
|
// find raw .map surface under Z coordinates
|
|
float mapHeight;
|
|
if(GridMap* gmap = GridMaps[gx][gy])
|
|
{
|
|
int lx_int = (int)lx;
|
|
int ly_int = (int)ly;
|
|
|
|
float zi[4];
|
|
// Probe 4 nearest points (except border cases)
|
|
zi[0] = gmap->Z[lx_int][ly_int];
|
|
zi[1] = lx < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int] : zi[0];
|
|
zi[2] = ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int][ly_int+1] : zi[0];
|
|
zi[3] = lx < MAP_RESOLUTION-1 && ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int+1] : zi[0];
|
|
// Recalculate them like if their x,y positions were in the range 0,1
|
|
float b[4];
|
|
b[0] = zi[0];
|
|
b[1] = zi[1]-zi[0];
|
|
b[2] = zi[2]-zi[0];
|
|
b[3] = zi[0]-zi[1]-zi[2]+zi[3];
|
|
// Normalize the dx and dy to be in range 0..1
|
|
float fact_x = lx - lx_int;
|
|
float fact_y = ly - ly_int;
|
|
// Use the simplified bilinear equation, as described in [url="http://en.wikipedia.org/wiki/Bilinear_interpolation"]http://en.wikipedia.org/wiki/Bilinear_interpolation[/url]
|
|
float _mapheight = b[0] + (b[1]*fact_x) + (b[2]*fact_y) + (b[3]*fact_x*fact_y);
|
|
|
|
// look from a bit higher pos to find the floor, ignore under surface case
|
|
if(z + 2.0f > _mapheight)
|
|
mapHeight = _mapheight;
|
|
else
|
|
mapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
|
}
|
|
else
|
|
mapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
|
|
|
float vmapHeight;
|
|
if(pUseVmaps)
|
|
{
|
|
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
|
|
if(vmgr->isHeightCalcEnabled())
|
|
{
|
|
// look from a bit higher pos to find the floor
|
|
vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f);
|
|
}
|
|
else
|
|
vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
|
}
|
|
else
|
|
vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
|
|
|
// mapHeight set for any above raw ground Z or <= INVALID_HEIGHT
|
|
// vmapheight set for any under Z value or <= INVALID_HEIGHT
|
|
|
|
if( vmapHeight > INVALID_HEIGHT )
|
|
{
|
|
if( mapHeight > INVALID_HEIGHT )
|
|
{
|
|
// we have mapheight and vmapheight and must select more appropriate
|
|
|
|
// we are already under the surface or vmap height above map heigt
|
|
// or if the distance of the vmap height is less the land height distance
|
|
if( z < mapHeight || vmapHeight > mapHeight || fabs(mapHeight-z) > fabs(vmapHeight-z) )
|
|
return vmapHeight;
|
|
else
|
|
return mapHeight; // better use .map surface height
|
|
|
|
}
|
|
else
|
|
return vmapHeight; // we have only vmapHeight (if have)
|
|
}
|
|
else
|
|
{
|
|
if(!pUseVmaps)
|
|
return mapHeight; // explicitly use map data (if have)
|
|
else if(mapHeight > INVALID_HEIGHT && (z < mapHeight + 2 || z == MAX_HEIGHT))
|
|
return mapHeight; // explicitly use map data if original z < mapHeight but map found (z+2 > mapHeight)
|
|
else
|
|
return VMAP_INVALID_HEIGHT_VALUE; // we not have any height
|
|
}
|
|
}
|
|
|
|
uint16 Map::GetAreaFlag(float x, float y ) const
|
|
{
|
|
//local x,y coords
|
|
float lx,ly;
|
|
int gx,gy;
|
|
GridPair p = Trinity::ComputeGridPair(x, y);
|
|
|
|
// half opt method
|
|
gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x
|
|
gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
|
|
|
|
lx=16*(32 -x/SIZE_OF_GRIDS - gx);
|
|
ly=16*(32 -y/SIZE_OF_GRIDS - gy);
|
|
//DEBUG_LOG("my %d %d si %d %d",gx,gy,p.x_coord,p.y_coord);
|
|
|
|
// ensure GridMap is loaded
|
|
const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
|
|
|
|
if(GridMaps[gx][gy])
|
|
return GridMaps[gx][gy]->area_flag[(int)(lx)][(int)(ly)];
|
|
// this used while not all *.map files generated (instances)
|
|
else
|
|
return GetAreaFlagByMapId(i_id);
|
|
}
|
|
|
|
uint8 Map::GetTerrainType(float x, float y ) const
|
|
{
|
|
//local x,y coords
|
|
float lx,ly;
|
|
int gx,gy;
|
|
|
|
// half opt method
|
|
gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x
|
|
gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
|
|
|
|
lx=16*(32 -x/SIZE_OF_GRIDS - gx);
|
|
ly=16*(32 -y/SIZE_OF_GRIDS - gy);
|
|
|
|
// ensure GridMap is loaded
|
|
const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
|
|
|
|
if(GridMaps[gx][gy])
|
|
return GridMaps[gx][gy]->terrain_type[(int)(lx)][(int)(ly)];
|
|
else
|
|
return 0;
|
|
|
|
}
|
|
|
|
float Map::GetWaterLevel(float x, float y ) const
|
|
{
|
|
//local x,y coords
|
|
float lx,ly;
|
|
int gx,gy;
|
|
|
|
// half opt method
|
|
gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x
|
|
gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
|
|
|
|
lx=128*(32 -x/SIZE_OF_GRIDS - gx);
|
|
ly=128*(32 -y/SIZE_OF_GRIDS - gy);
|
|
|
|
// ensure GridMap is loaded
|
|
const_cast<Map*>(this)->EnsureGridCreated(GridPair(63-gx,63-gy));
|
|
|
|
if(GridMaps[gx][gy])
|
|
return GridMaps[gx][gy]->liquid_level[(int)(lx)][(int)(ly)];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id)
|
|
{
|
|
AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
|
|
|
|
if (entry)
|
|
return entry->ID;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id)
|
|
{
|
|
AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
|
|
|
|
if( entry )
|
|
return ( entry->zone != 0 ) ? entry->zone : entry->ID;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
bool Map::IsInWater(float x, float y, float pZ) const
|
|
{
|
|
// This method is called too often to use vamps for that (4. parameter = false).
|
|
// The pZ pos is taken anyway for future use
|
|
float z = GetHeight(x,y,pZ,false); // use .map base surface height
|
|
|
|
// underground or instance without vmap
|
|
if(z <= INVALID_HEIGHT)
|
|
return false;
|
|
|
|
float water_z = GetWaterLevel(x,y);
|
|
uint8 flag = GetTerrainType(x,y);
|
|
return (z < (water_z-2)) && (flag & 0x01);
|
|
}
|
|
|
|
bool Map::IsUnderWater(float x, float y, float z) const
|
|
{
|
|
float water_z = GetWaterLevel(x,y);
|
|
uint8 flag = GetTerrainType(x,y);
|
|
return (z < (water_z-2)) && (flag & 0x01);
|
|
}
|
|
|
|
bool Map::CheckGridIntegrity(Creature* c, bool moved) const
|
|
{
|
|
Cell const& cur_cell = c->GetCurrentCell();
|
|
|
|
CellPair xy_val = Trinity::ComputeCellPair(c->GetPositionX(), c->GetPositionY());
|
|
Cell xy_cell(xy_val);
|
|
if(xy_cell != cur_cell)
|
|
{
|
|
sLog.outError("ERROR: %s (GUID: %u) X: %f Y: %f (%s) in grid[%u,%u]cell[%u,%u] instead grid[%u,%u]cell[%u,%u]",
|
|
(c->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature"),c->GetGUIDLow(),
|
|
c->GetPositionX(),c->GetPositionY(),(moved ? "final" : "original"),
|
|
cur_cell.GridX(), cur_cell.GridY(), cur_cell.CellX(), cur_cell.CellY(),
|
|
xy_cell.GridX(), xy_cell.GridY(), xy_cell.CellX(), xy_cell.CellY());
|
|
return true; // not crash at error, just output error in debug mode
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const char* Map::GetMapName() const
|
|
{
|
|
return i_mapEntry ? i_mapEntry->name[sWorld.GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
|
|
}
|
|
|
|
void Map::UpdateObjectVisibility( WorldObject* obj, Cell cell, CellPair cellpair)
|
|
{
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
cell.SetNoCreate();
|
|
Trinity::VisibleChangesNotifier notifier(*obj);
|
|
TypeContainerVisitor<Trinity::VisibleChangesNotifier, WorldTypeMapContainer > player_notifier(notifier);
|
|
CellLock<GridReadGuard> cell_lock(cell, cellpair);
|
|
cell_lock->Visit(cell_lock, player_notifier, *this);
|
|
}
|
|
|
|
void Map::UpdatePlayerVisibility( Player* player, Cell cell, CellPair cellpair )
|
|
{
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
|
|
Trinity::PlayerNotifier pl_notifier(*player);
|
|
TypeContainerVisitor<Trinity::PlayerNotifier, WorldTypeMapContainer > player_notifier(pl_notifier);
|
|
|
|
CellLock<ReadGuard> cell_lock(cell, cellpair);
|
|
cell_lock->Visit(cell_lock, player_notifier, *this);
|
|
}
|
|
|
|
void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair )
|
|
{
|
|
Trinity::VisibleNotifier notifier(*player);
|
|
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
cell.SetNoCreate();
|
|
TypeContainerVisitor<Trinity::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
|
|
TypeContainerVisitor<Trinity::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
|
|
CellLock<GridReadGuard> cell_lock(cell, cellpair);
|
|
cell_lock->Visit(cell_lock, world_notifier, *this);
|
|
cell_lock->Visit(cell_lock, grid_notifier, *this);
|
|
|
|
// send data
|
|
notifier.Notify();
|
|
}
|
|
|
|
void Map::PlayerRelocationNotify( Player* player, Cell cell, CellPair cellpair )
|
|
{
|
|
CellLock<ReadGuard> cell_lock(cell, cellpair);
|
|
Trinity::PlayerRelocationNotifier relocationNotifier(*player);
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
|
|
TypeContainerVisitor<Trinity::PlayerRelocationNotifier, GridTypeMapContainer > p2grid_relocation(relocationNotifier);
|
|
TypeContainerVisitor<Trinity::PlayerRelocationNotifier, WorldTypeMapContainer > p2world_relocation(relocationNotifier);
|
|
|
|
cell_lock->Visit(cell_lock, p2grid_relocation, *this);
|
|
cell_lock->Visit(cell_lock, p2world_relocation, *this);
|
|
}
|
|
|
|
void Map::CreatureRelocationNotify(Creature *creature, Cell cell, CellPair cellpair)
|
|
{
|
|
CellLock<ReadGuard> cell_lock(cell, cellpair);
|
|
Trinity::CreatureRelocationNotifier relocationNotifier(*creature);
|
|
cell.data.Part.reserved = ALL_DISTRICT;
|
|
cell.SetNoCreate(); // not trigger load unloaded grids at notifier call
|
|
|
|
TypeContainerVisitor<Trinity::CreatureRelocationNotifier, WorldTypeMapContainer > c2world_relocation(relocationNotifier);
|
|
TypeContainerVisitor<Trinity::CreatureRelocationNotifier, GridTypeMapContainer > c2grid_relocation(relocationNotifier);
|
|
|
|
cell_lock->Visit(cell_lock, c2world_relocation, *this);
|
|
cell_lock->Visit(cell_lock, c2grid_relocation, *this);
|
|
}
|
|
|
|
void Map::SendInitSelf( Player * player )
|
|
{
|
|
sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow());
|
|
|
|
UpdateData data;
|
|
|
|
bool hasTransport = false;
|
|
|
|
// attach to player data current transport data
|
|
if(Transport* transport = player->GetTransport())
|
|
{
|
|
hasTransport = true;
|
|
transport->BuildCreateUpdateBlockForPlayer(&data, player);
|
|
}
|
|
|
|
// build data for self presence in world at own client (one time for map)
|
|
player->BuildCreateUpdateBlockForPlayer(&data, player);
|
|
|
|
// build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map
|
|
if(Transport* transport = player->GetTransport())
|
|
{
|
|
for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr)
|
|
{
|
|
if(player!=(*itr) && player->HaveAtClient(*itr))
|
|
{
|
|
hasTransport = true;
|
|
(*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
|
|
}
|
|
}
|
|
}
|
|
|
|
WorldPacket packet;
|
|
data.BuildPacket(&packet, hasTransport);
|
|
player->GetSession()->SendPacket(&packet);
|
|
}
|
|
|
|
void Map::SendInitTransports( Player * player )
|
|
{
|
|
// Hack to send out transports
|
|
MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
|
|
|
|
// no transports at map
|
|
if (tmap.find(player->GetMapId()) == tmap.end())
|
|
return;
|
|
|
|
UpdateData transData;
|
|
|
|
MapManager::TransportSet& tset = tmap[player->GetMapId()];
|
|
|
|
bool hasTransport = false;
|
|
|
|
for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i)
|
|
{
|
|
if((*i) != player->GetTransport()) // send data for current transport in other place
|
|
{
|
|
hasTransport = true;
|
|
(*i)->BuildCreateUpdateBlockForPlayer(&transData, player);
|
|
}
|
|
}
|
|
|
|
WorldPacket packet;
|
|
transData.BuildPacket(&packet, hasTransport);
|
|
player->GetSession()->SendPacket(&packet);
|
|
}
|
|
|
|
void Map::SendRemoveTransports( Player * player )
|
|
{
|
|
// Hack to send out transports
|
|
MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap;
|
|
|
|
// no transports at map
|
|
if (tmap.find(player->GetMapId()) == tmap.end())
|
|
return;
|
|
|
|
UpdateData transData;
|
|
|
|
MapManager::TransportSet& tset = tmap[player->GetMapId()];
|
|
|
|
// except used transport
|
|
for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i)
|
|
if(player->GetTransport() != (*i))
|
|
(*i)->BuildOutOfRangeUpdateBlock(&transData);
|
|
|
|
WorldPacket packet;
|
|
transData.BuildPacket(&packet);
|
|
player->GetSession()->SendPacket(&packet);
|
|
}
|
|
|
|
inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y)
|
|
{
|
|
if(x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS)
|
|
{
|
|
sLog.outError("map::setNGrid() Invalid grid coordinates found: %d, %d!",x,y);
|
|
assert(false);
|
|
}
|
|
i_grids[x][y] = grid;
|
|
}
|
|
|
|
void Map::DoDelayedMovesAndRemoves()
|
|
{
|
|
MoveAllCreaturesInMoveList();
|
|
RemoveAllObjectsInRemoveList();
|
|
}
|
|
|
|
void Map::AddObjectToRemoveList(WorldObject *obj)
|
|
{
|
|
assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId());
|
|
|
|
i_objectsToRemove.insert(obj);
|
|
//sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId());
|
|
}
|
|
|
|
void Map::RemoveAllObjectsInRemoveList()
|
|
{
|
|
if(i_objectsToRemove.empty())
|
|
return;
|
|
|
|
//sLog.outDebug("Object remover 1 check.");
|
|
while(!i_objectsToRemove.empty())
|
|
{
|
|
WorldObject* obj = *i_objectsToRemove.begin();
|
|
i_objectsToRemove.erase(i_objectsToRemove.begin());
|
|
|
|
switch(obj->GetTypeId())
|
|
{
|
|
case TYPEID_CORPSE:
|
|
{
|
|
Corpse* corpse = ObjectAccessor::Instance().GetCorpse(*obj, obj->GetGUID());
|
|
if (!corpse)
|
|
sLog.outError("ERROR: Try delete corpse/bones %u that not in map", obj->GetGUIDLow());
|
|
else
|
|
Remove(corpse,true);
|
|
break;
|
|
}
|
|
case TYPEID_DYNAMICOBJECT:
|
|
Remove((DynamicObject*)obj,true);
|
|
break;
|
|
case TYPEID_GAMEOBJECT:
|
|
Remove((GameObject*)obj,true);
|
|
break;
|
|
case TYPEID_UNIT:
|
|
Remove((Creature*)obj,true);
|
|
break;
|
|
default:
|
|
sLog.outError("Non-grid object (TypeId: %u) in grid object removing list, ignored.",obj->GetTypeId());
|
|
break;
|
|
}
|
|
}
|
|
//sLog.outDebug("Object remover 2 check.");
|
|
}
|
|
|
|
bool Map::CanUnload(const uint32 &diff)
|
|
{
|
|
if(!m_unloadTimer) return false;
|
|
if(m_unloadTimer < diff) return true;
|
|
m_unloadTimer -= diff;
|
|
return false;
|
|
}
|
|
|
|
template void Map::Add(Corpse *);
|
|
template void Map::Add(Creature *);
|
|
template void Map::Add(GameObject *);
|
|
template void Map::Add(DynamicObject *);
|
|
|
|
template void Map::Remove(Corpse *,bool);
|
|
template void Map::Remove(Creature *,bool);
|
|
template void Map::Remove(GameObject *, bool);
|
|
template void Map::Remove(DynamicObject *, bool);
|
|
|
|
/* ******* Dungeon Instance Maps ******* */
|
|
|
|
InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode)
|
|
: Map(id, expiry, InstanceId, SpawnMode), i_data(NULL),
|
|
m_resetAfterUnload(false), m_unloadWhenEmpty(false)
|
|
{
|
|
// the timer is started by default, and stopped when the first player joins
|
|
// this make sure it gets unloaded if for some reason no player joins
|
|
m_unloadTimer = std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
|
|
}
|
|
|
|
InstanceMap::~InstanceMap()
|
|
{
|
|
if(i_data)
|
|
{
|
|
delete i_data;
|
|
i_data = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Do map specific checks to see if the player can enter
|
|
*/
|
|
bool InstanceMap::CanEnter(Player *player)
|
|
{
|
|
if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end())
|
|
{
|
|
sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
// cannot enter if the instance is full (player cap), GMs don't count
|
|
InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId());
|
|
if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= iTemplate->maxPlayers)
|
|
{
|
|
sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), iTemplate->maxPlayers, player->GetName());
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
|
|
return false;
|
|
}
|
|
|
|
// cannot enter while players in the instance are in combat
|
|
Group *pGroup = player->GetGroup();
|
|
if(!player->isGameMaster() && pGroup && pGroup->InCombatToInstance(GetInstanceId()) && player->isAlive() && player->GetMapId() != GetId())
|
|
{
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
|
|
return false;
|
|
}
|
|
|
|
/*if(!player->isGameMaster() && i_data && i_data->IsEncounterInProgress())
|
|
{
|
|
sLog.outDebug("InstanceMap::CanEnter - 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 Map::CanEnter(player);
|
|
}
|
|
|
|
/*
|
|
Do map specific checks and add the player to the map if successful.
|
|
*/
|
|
bool InstanceMap::Add(Player *player)
|
|
{
|
|
// TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode
|
|
// GMs still can teleport player in instance.
|
|
// Is it needed?
|
|
|
|
{
|
|
Guard guard(*this);
|
|
if(!CanEnter(player))
|
|
return false;
|
|
|
|
// get or create an instance save for the map
|
|
InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
|
|
if(!mapSave)
|
|
{
|
|
sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId());
|
|
mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true);
|
|
}
|
|
|
|
// check for existing instance binds
|
|
InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode());
|
|
if(playerBind && playerBind->perm)
|
|
{
|
|
// cannot enter other instances if bound permanently
|
|
if(playerBind->save != mapSave)
|
|
{
|
|
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());
|
|
assert(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Group *pGroup = player->GetGroup();
|
|
if(pGroup)
|
|
{
|
|
// solo saves should be reset when entering a group
|
|
InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode());
|
|
if(playerBind)
|
|
{
|
|
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());
|
|
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());
|
|
assert(false);
|
|
}
|
|
// bind to the group or keep using the group save
|
|
if(!groupBind)
|
|
pGroup->BindToInstance(mapSave, false);
|
|
else
|
|
{
|
|
// cannot jump to a different instance without resetting it
|
|
if(groupBind->save != mapSave)
|
|
{
|
|
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());
|
|
if(mapSave)
|
|
sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
|
|
else
|
|
sLog.outError("MapSave NULL");
|
|
if(groupBind->save)
|
|
sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount());
|
|
else
|
|
sLog.outError("GroupBind save NULL");
|
|
assert(false);
|
|
}
|
|
// if the group/leader is permanently bound to the instance
|
|
// players also become permanently bound when they enter
|
|
if(groupBind->perm)
|
|
{
|
|
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
|
|
data << uint32(0);
|
|
player->GetSession()->SendPacket(&data);
|
|
player->BindToInstance(mapSave, true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// set up a solo bind or continue using it
|
|
if(!playerBind)
|
|
player->BindToInstance(mapSave, false);
|
|
else
|
|
// cannot jump to a different instance without resetting it
|
|
assert(playerBind->save == mapSave);
|
|
}
|
|
}
|
|
|
|
if(i_data) i_data->OnPlayerEnter(player);
|
|
SetResetSchedule(false);
|
|
|
|
i_Players.push_back(player);
|
|
player->SendInitWorldStates();
|
|
sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName());
|
|
// initialize unload state
|
|
m_unloadTimer = 0;
|
|
m_resetAfterUnload = false;
|
|
m_unloadWhenEmpty = false;
|
|
}
|
|
|
|
// this will acquire the same mutex so it cannot be in the previous block
|
|
Map::Add(player);
|
|
return true;
|
|
}
|
|
|
|
void InstanceMap::Remove(Player *player, bool remove)
|
|
{
|
|
sLog.outDetail("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
|
|
i_Players.remove(player);
|
|
SetResetSchedule(true);
|
|
if(!m_unloadTimer && i_Players.empty())
|
|
m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
|
|
Map::Remove(player, remove);
|
|
}
|
|
|
|
Creature * Map::GetCreatureInMap(uint64 guid)
|
|
{
|
|
Creature * obj = HashMapHolder<Creature>::Find(guid);
|
|
if(obj && obj->GetInstanceId() != GetInstanceId()) obj = NULL;
|
|
return obj;
|
|
}
|
|
|
|
GameObject * Map::GetGameObjectInMap(uint64 guid)
|
|
{
|
|
GameObject * obj = HashMapHolder<GameObject>::Find(guid);
|
|
if(obj && obj->GetInstanceId() != GetInstanceId()) obj = NULL;
|
|
return obj;
|
|
}
|
|
|
|
void InstanceMap::CreateInstanceData(bool load)
|
|
{
|
|
if(i_data != NULL)
|
|
return;
|
|
|
|
InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(GetId());
|
|
if (mInstance)
|
|
{
|
|
i_script = mInstance->script;
|
|
i_data = Script->CreateInstanceData(this);
|
|
}
|
|
|
|
if(!i_data)
|
|
return;
|
|
|
|
if(load)
|
|
{
|
|
// TODO: make a global storage for this
|
|
QueryResult* result = CharacterDatabase.PQuery("SELECT data FROM instance WHERE map = '%u' AND id = '%u'", GetId(), i_InstanceId);
|
|
if (result)
|
|
{
|
|
Field* fields = result->Fetch();
|
|
const char* data = fields[0].GetString();
|
|
if(data)
|
|
{
|
|
sLog.outDebug("Loading instance data for `%s` with id %u", i_script.c_str(), i_InstanceId);
|
|
i_data->Load(data);
|
|
}
|
|
delete result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sLog.outDebug("New instance data, \"%s\" ,initialized!",i_script.c_str());
|
|
i_data->Initialize();
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns true if there are no players in the instance
|
|
*/
|
|
bool InstanceMap::Reset(uint8 method)
|
|
{
|
|
// note: since the map may not be loaded when the instance needs to be reset
|
|
// the instance must be deleted from the DB by InstanceSaveManager
|
|
|
|
if(!i_Players.empty())
|
|
{
|
|
if(method == INSTANCE_RESET_ALL)
|
|
{
|
|
// notify the players to leave the instance so it can be reset
|
|
for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
(*itr)->SendResetFailedNotify(GetId());
|
|
}
|
|
else
|
|
{
|
|
if(method == INSTANCE_RESET_GLOBAL)
|
|
{
|
|
// set the homebind timer for players inside (1 minute)
|
|
for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
(*itr)->m_InstanceValid = false;
|
|
}
|
|
|
|
// the unload timer is not started
|
|
// instead the map will unload immediately after the players have left
|
|
m_unloadWhenEmpty = true;
|
|
m_resetAfterUnload = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// unloaded at next update
|
|
m_unloadTimer = MIN_UNLOAD_DELAY;
|
|
m_resetAfterUnload = true;
|
|
}
|
|
|
|
return i_Players.empty();
|
|
}
|
|
|
|
uint32 InstanceMap::GetPlayersCountExceptGMs() const
|
|
{
|
|
uint32 count = 0;
|
|
for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
if(!(*itr)->isGameMaster())
|
|
++count;
|
|
return count;
|
|
}
|
|
|
|
void InstanceMap::PermBindAllPlayers(Player *player)
|
|
{
|
|
InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
|
|
if(!save)
|
|
{
|
|
sLog.outError("Cannot bind players, no instance save available for map!\n");
|
|
return;
|
|
}
|
|
|
|
Group *group = player->GetGroup();
|
|
// group members outside the instance group don't get bound
|
|
for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
{
|
|
if(*itr)
|
|
{
|
|
// players inside an instance cannot be bound to other instances
|
|
// some players may already be permanently bound, in this case nothing happens
|
|
InstancePlayerBind *bind = (*itr)->GetBoundInstance(save->GetMapId(), save->GetDifficulty());
|
|
if(!bind || !bind->perm)
|
|
{
|
|
(*itr)->BindToInstance(save, true);
|
|
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
|
|
data << uint32(0);
|
|
(*itr)->GetSession()->SendPacket(&data);
|
|
}
|
|
|
|
// if the leader is not in the instance the group will not get a perm bind
|
|
if(group && group->GetLeaderGUID() == (*itr)->GetGUID())
|
|
group->BindToInstance(save, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
time_t InstanceMap::GetResetTime()
|
|
{
|
|
InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
|
|
return save ? save->GetDifficulty() : DIFFICULTY_NORMAL;
|
|
}
|
|
|
|
void InstanceMap::UnloadAll(bool pForce)
|
|
{
|
|
if(!i_Players.empty())
|
|
{
|
|
sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!");
|
|
for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
if(*itr) (*itr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation());
|
|
}
|
|
|
|
if(m_resetAfterUnload == true)
|
|
objmgr.DeleteRespawnTimeForInstance(GetInstanceId());
|
|
|
|
Map::UnloadAll(pForce);
|
|
}
|
|
|
|
void InstanceMap::SendResetWarnings(uint32 timeLeft)
|
|
{
|
|
for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
(*itr)->SendInstanceResetWarning(GetId(), timeLeft);
|
|
}
|
|
|
|
void InstanceMap::SetResetSchedule(bool on)
|
|
{
|
|
// only for normal instances
|
|
// the reset time is only scheduled when there are no payers inside
|
|
// it is assumed that the reset time will rarely (if ever) change while the reset is scheduled
|
|
if(i_Players.empty() && !IsRaid() && !IsHeroic())
|
|
{
|
|
InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
|
|
if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId());
|
|
else sInstanceSaveManager.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), GetInstanceId()));
|
|
}
|
|
}
|
|
|
|
void InstanceMap::SendToPlayers(WorldPacket const* data) const
|
|
{
|
|
for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr)
|
|
(*itr)->GetSession()->SendPacket(data);
|
|
}
|
|
|
|
/* ******* Battleground Instance Maps ******* */
|
|
|
|
BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId)
|
|
: Map(id, expiry, InstanceId, DIFFICULTY_NORMAL)
|
|
{
|
|
}
|
|
|
|
BattleGroundMap::~BattleGroundMap()
|
|
{
|
|
}
|
|
|
|
bool BattleGroundMap::CanEnter(Player * player)
|
|
{
|
|
if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end())
|
|
{
|
|
sLog.outError("BGMap::CanEnter - player %u already in map!", player->GetGUIDLow());
|
|
assert(false);
|
|
return false;
|
|
}
|
|
|
|
if(player->GetBattleGroundId() != GetInstanceId())
|
|
return false;
|
|
|
|
// player number limit is checked in bgmgr, no need to do it here
|
|
|
|
return Map::CanEnter(player);
|
|
}
|
|
|
|
bool BattleGroundMap::Add(Player * player)
|
|
{
|
|
{
|
|
Guard guard(*this);
|
|
if(!CanEnter(player))
|
|
return false;
|
|
i_Players.push_back(player);
|
|
// reset instance validity, battleground maps do not homebind
|
|
player->m_InstanceValid = true;
|
|
}
|
|
return Map::Add(player);
|
|
}
|
|
|
|
void BattleGroundMap::Remove(Player *player, bool remove)
|
|
{
|
|
sLog.outDetail("MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName());
|
|
i_Players.remove(player);
|
|
Map::Remove(player, remove);
|
|
}
|
|
|
|
void BattleGroundMap::SetUnload()
|
|
{
|
|
m_unloadTimer = MIN_UNLOAD_DELAY;
|
|
}
|
|
|
|
void BattleGroundMap::UnloadAll(bool pForce)
|
|
{
|
|
while(!i_Players.empty())
|
|
{
|
|
PlayerList::iterator itr = i_Players.begin();
|
|
Player * plr = *itr;
|
|
if(plr) (plr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation());
|
|
// TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator.
|
|
// just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop
|
|
// note that this remove is not needed if the code works well in other places
|
|
i_Players.remove(plr);
|
|
}
|
|
|
|
Map::UnloadAll(pForce);
|
|
}
|