mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 17:19:07 +00:00
For a more educated fix would need to get access to git history for why the hook was moved here. Or alternatively, try research what would be the correct place for the hook.
3290 lines
104 KiB
C++
3290 lines
104 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: http://github.com/azerothcore/azerothcore-wotlk/LICENSE-GPL2
|
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*/
|
|
|
|
#include "Map.h"
|
|
#include "Battleground.h"
|
|
#include "CellImpl.h"
|
|
#include "DynamicTree.h"
|
|
#include "GridNotifiers.h"
|
|
#include "GridNotifiersImpl.h"
|
|
#include "Object.h"
|
|
#include "Group.h"
|
|
#include "InstanceScript.h"
|
|
#include "MapInstanced.h"
|
|
#include "MapManager.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "ObjectMgr.h"
|
|
#include "Pet.h"
|
|
#include "ScriptMgr.h"
|
|
#include "Transport.h"
|
|
#include "Vehicle.h"
|
|
#include "VMapFactory.h"
|
|
#include "LFGMgr.h"
|
|
#include "Chat.h"
|
|
|
|
union u_map_magic
|
|
{
|
|
char asChar[4];
|
|
uint32 asUInt;
|
|
};
|
|
|
|
u_map_magic MapMagic = { {'M','A','P','S'} };
|
|
u_map_magic MapVersionMagic = { {'v','1','.','8'} };
|
|
u_map_magic MapAreaMagic = { {'A','R','E','A'} };
|
|
u_map_magic MapHeightMagic = { {'M','H','G','T'} };
|
|
u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
|
|
|
|
Map::~Map()
|
|
{
|
|
sScriptMgr->OnDestroyMap(this);
|
|
|
|
UnloadAll();
|
|
|
|
while (!i_worldObjects.empty())
|
|
{
|
|
WorldObject* obj = *i_worldObjects.begin();
|
|
ASSERT(obj->IsWorldObject());
|
|
//ASSERT(obj->GetTypeId() == TYPEID_CORPSE);
|
|
obj->RemoveFromWorld();
|
|
obj->ResetMap();
|
|
}
|
|
|
|
if (!m_scriptSchedule.empty())
|
|
sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
|
|
|
|
//MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId());
|
|
MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
|
|
}
|
|
|
|
bool Map::ExistMap(uint32 mapid, int gx, int gy)
|
|
{
|
|
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, gx, gy);
|
|
|
|
bool ret = false;
|
|
FILE* pf=fopen(tmp, "rb");
|
|
|
|
if (!pf)
|
|
sLog->outError("Map file '%s': does not exist!", tmp);
|
|
else
|
|
{
|
|
map_fileheader header;
|
|
if (fread(&header, sizeof(header), 1, pf) == 1)
|
|
{
|
|
if (header.mapMagic != MapMagic.asUInt || header.versionMagic != MapVersionMagic.asUInt)
|
|
sLog->outError("Map file '%s' is from an incompatible clientversion. Please recreate using the mapextractor.", tmp);
|
|
else
|
|
ret = true;
|
|
}
|
|
fclose(pf);
|
|
}
|
|
delete [] tmp;
|
|
return ret;
|
|
}
|
|
|
|
bool Map::ExistVMap(uint32 mapid, int gx, int gy)
|
|
{
|
|
if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
|
|
{
|
|
if (vmgr->isMapLoadingEnabled())
|
|
{
|
|
bool exists = vmgr->existsMap((sWorld->GetDataPath()+ "vmaps").c_str(), mapid, gx, gy);
|
|
if (!exists)
|
|
{
|
|
std::string name = vmgr->getDirFileName(mapid, gx, gy);
|
|
sLog->outError("VMap file '%s' is missing or points to wrong version of vmap file. Redo vmaps with latest version of vmap_assembler.exe.", (sWorld->GetDataPath()+"vmaps/"+name).c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Map::LoadMMap(int gx, int gy)
|
|
{
|
|
if (!MMAP::MMapFactory::IsPathfindingEnabled(this)) // pussywizard
|
|
return;
|
|
|
|
int mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap(GetId(), gx, gy);
|
|
switch (mmapLoadResult)
|
|
{
|
|
case MMAP::MMAP_LOAD_RESULT_OK:
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("MMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
|
|
#endif
|
|
break;
|
|
case MMAP::MMAP_LOAD_RESULT_ERROR:
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("Could not load MMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
|
|
#endif
|
|
break;
|
|
case MMAP::MMAP_LOAD_RESULT_IGNORED:
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outStaticDebug("Ignored MMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Map::LoadVMap(int gx, int gy)
|
|
{
|
|
// x and y are swapped !!
|
|
int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld->GetDataPath()+ "vmaps").c_str(), GetId(), gx, gy);
|
|
switch (vmapLoadResult)
|
|
{
|
|
case VMAP::VMAP_LOAD_RESULT_OK:
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
|
|
#endif
|
|
break;
|
|
case VMAP::VMAP_LOAD_RESULT_ERROR:
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
|
|
#endif
|
|
break;
|
|
case VMAP::VMAP_LOAD_RESULT_IGNORED:
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outStaticDebug("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Map::LoadMap(int gx, int gy, bool reload)
|
|
{
|
|
if (i_InstanceId != 0)
|
|
{
|
|
if (GridMaps[gx][gy])
|
|
return;
|
|
|
|
// load grid map for base map
|
|
m_parentMap->EnsureGridCreated(GridCoord(63-gx, 63-gy));
|
|
|
|
GridMaps[gx][gy] = m_parentMap->GridMaps[gx][gy];
|
|
return;
|
|
}
|
|
|
|
if (GridMaps[gx][gy] && !reload)
|
|
return;
|
|
|
|
//map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?)
|
|
if (GridMaps[gx][gy])
|
|
{
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("Unloading previously loaded map %u before reloading.", GetId());
|
|
#endif
|
|
sScriptMgr->OnUnloadGridMap(this, GridMaps[gx][gy], gx, gy);
|
|
|
|
delete (GridMaps[gx][gy]);
|
|
GridMaps[gx][gy]=NULL;
|
|
}
|
|
|
|
// map file name
|
|
char *tmp=NULL;
|
|
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(), GetId(), gx, gy);
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("Loading map %s", tmp);
|
|
#endif
|
|
// loading data
|
|
GridMaps[gx][gy] = new GridMap();
|
|
if (!GridMaps[gx][gy]->loadData(tmp))
|
|
{
|
|
sLog->outError("Error loading map file: \n %s\n", tmp);
|
|
}
|
|
delete [] tmp;
|
|
|
|
sScriptMgr->OnLoadGridMap(this, GridMaps[gx][gy], gx, gy);
|
|
}
|
|
|
|
void Map::LoadMapAndVMap(int gx, int gy)
|
|
{
|
|
LoadMap(gx, gy);
|
|
if (i_InstanceId == 0)
|
|
{
|
|
LoadVMap(gx, gy); // Only load the data for the base map
|
|
LoadMMap(gx, gy);
|
|
}
|
|
}
|
|
|
|
Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) :
|
|
i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
|
|
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
|
|
_instanceResetPeriod(0), m_activeNonPlayersIter(m_activeNonPlayers.end()),
|
|
_transportsUpdateIter(_transports.end()), i_scriptLock(false), _defaultLight(GetDefaultMapLight(id))
|
|
{
|
|
m_parentMap = (_parent ? _parent : this);
|
|
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);
|
|
}
|
|
}
|
|
|
|
//lets initialize visibility distance for map
|
|
Map::InitVisibilityDistance();
|
|
|
|
sScriptMgr->OnCreateMap(this);
|
|
}
|
|
|
|
void Map::InitVisibilityDistance()
|
|
{
|
|
//init visibility for continents
|
|
m_VisibleDistance = World::GetMaxVisibleDistanceOnContinents();
|
|
|
|
switch (GetId())
|
|
{
|
|
case 609: // Scarlet Enclave (DK starting zone)
|
|
m_VisibleDistance = 125.0f;
|
|
break;
|
|
case 25: // Scott Test (box map)
|
|
m_VisibleDistance = 200.0f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Template specialization of utility methods
|
|
template<class T>
|
|
void Map::AddToGrid(T* obj, Cell const& cell)
|
|
{
|
|
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
|
if (obj->IsWorldObject())
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).template AddWorldObject<T>(obj);
|
|
else
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj);
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToGrid(Creature* obj, Cell const& cell)
|
|
{
|
|
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
|
if (obj->IsWorldObject())
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj);
|
|
else
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
|
|
|
obj->SetCurrentCell(cell);
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToGrid(GameObject* obj, Cell const& cell)
|
|
{
|
|
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
|
|
|
obj->SetCurrentCell(cell);
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToGrid(DynamicObject* obj, Cell const& cell)
|
|
{
|
|
NGridType* grid = getNGrid(cell.GridX(), cell.GridY());
|
|
if (obj->IsWorldObject())
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).AddWorldObject(obj);
|
|
else
|
|
grid->GetGridType(cell.CellX(), cell.CellY()).AddGridObject(obj);
|
|
|
|
obj->SetCurrentCell(cell);
|
|
}
|
|
|
|
template<class T>
|
|
void Map::SwitchGridContainers(T* /*obj*/, bool /*on*/)
|
|
{
|
|
}
|
|
|
|
template<>
|
|
void Map::SwitchGridContainers(Creature* obj, bool on)
|
|
{
|
|
ASSERT(!obj->IsPermanentWorldObject());
|
|
CellCoord p = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
|
|
if (!p.IsCoordValid())
|
|
{
|
|
sLog->outError("Map::SwitchGridContainers: Object " UI64FMTD " has 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 (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y)))
|
|
return;
|
|
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outStaticDebug("Switch object " UI64FMTD " from grid[%u, %u] %u", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y, on);
|
|
#endif
|
|
NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY());
|
|
ASSERT(ngrid != NULL);
|
|
|
|
GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY());
|
|
|
|
obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
|
|
|
|
if (on)
|
|
{
|
|
grid.AddWorldObject(obj);
|
|
AddWorldObject(obj);
|
|
}
|
|
else
|
|
{
|
|
grid.AddGridObject(obj);
|
|
RemoveWorldObject(obj);
|
|
}
|
|
|
|
obj->m_isTempWorldObject = on;
|
|
}
|
|
|
|
template<>
|
|
void Map::SwitchGridContainers(GameObject* obj, bool on)
|
|
{
|
|
ASSERT(!obj->IsPermanentWorldObject());
|
|
CellCoord p = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
|
|
if (!p.IsCoordValid())
|
|
{
|
|
sLog->outError("Map::SwitchGridContainers: Object " UI64FMTD " has 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 (!IsGridLoaded(GridCoord(cell.data.Part.grid_x, cell.data.Part.grid_y)))
|
|
return;
|
|
|
|
//TC_LOG_DEBUG(LOG_FILTER_MAPS, "Switch object " UI64FMTD " from grid[%u, %u] %u", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y, on);
|
|
NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY());
|
|
ASSERT(ngrid != NULL);
|
|
|
|
GridType &grid = ngrid->GetGridType(cell.CellX(), cell.CellY());
|
|
|
|
obj->RemoveFromGrid(); //This step is not really necessary but we want to do ASSERT in remove/add
|
|
|
|
if (on)
|
|
{
|
|
grid.AddWorldObject(obj);
|
|
AddWorldObject(obj);
|
|
}
|
|
else
|
|
{
|
|
grid.AddGridObject(obj);
|
|
RemoveWorldObject(obj);
|
|
}
|
|
}
|
|
|
|
template<class T>
|
|
void Map::DeleteFromWorld(T* obj)
|
|
{
|
|
// Note: In case resurrectable corpse and pet its removed from global lists in own destructor
|
|
delete obj;
|
|
}
|
|
|
|
template<>
|
|
void Map::DeleteFromWorld(Player* player)
|
|
{
|
|
sObjectAccessor->RemoveObject(player);
|
|
|
|
// pussywizard: optimization
|
|
std::string charName = player->GetName();
|
|
std::transform(charName.begin(), charName.end(), charName.begin(), ::tolower);
|
|
sObjectAccessor->playerNameToPlayerPointer.erase(charName);
|
|
|
|
sObjectAccessor->RemoveUpdateObject(player); //TODO: I do not know why we need this, it should be removed in ~Object anyway
|
|
delete player;
|
|
}
|
|
|
|
|
|
void Map::EnsureGridCreated(const GridCoord &p)
|
|
{
|
|
if (getNGrid(p.x_coord, p.y_coord)) // pussywizard
|
|
return;
|
|
TRINITY_GUARD(ACE_Thread_Mutex, GridLock);
|
|
EnsureGridCreated_i(p);
|
|
}
|
|
|
|
//Create NGrid so the object can be added to it
|
|
//But object data is not loaded here
|
|
void Map::EnsureGridCreated_i(const GridCoord &p)
|
|
{
|
|
if (!getNGrid(p.x_coord, p.y_coord))
|
|
{
|
|
// pussywizard: moved setNGrid to the end of the function
|
|
NGridType* ngt = new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord);
|
|
|
|
// build a linkage between this map and NGridType
|
|
buildNGridLinkage(ngt); // pussywizard: getNGrid(x, y) changed to: ngt
|
|
|
|
//z coord
|
|
int gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord;
|
|
int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord;
|
|
|
|
if (!GridMaps[gx][gy])
|
|
{
|
|
LoadMapAndVMap(gx, gy);
|
|
}
|
|
|
|
// pussywizard: moved here
|
|
setNGrid(ngt, p.x_coord, p.y_coord);
|
|
}
|
|
}
|
|
|
|
//Create NGrid and load the object data in it
|
|
bool Map::EnsureGridLoaded(const Cell &cell)
|
|
{
|
|
EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY()));
|
|
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
|
|
|
|
ASSERT(grid != NULL);
|
|
if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY()))
|
|
{
|
|
//if (!isGridObjectDataLoaded(cell.GridX(), cell.GridY()))
|
|
//{
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDebug(LOG_FILTER_MAPS, "Loading grid[%u, %u] for map %u instance %u", cell.GridX(), cell.GridY(), GetId(), i_InstanceId);
|
|
#endif
|
|
|
|
setGridObjectDataLoaded(true, cell.GridX(), cell.GridY());
|
|
|
|
ObjectGridLoader loader(*grid, this, cell);
|
|
loader.LoadN();
|
|
|
|
// Add resurrectable corpses to world object list in grid
|
|
sObjectAccessor->AddCorpsesToGrid(GridCoord(cell.GridX(), cell.GridY()), grid->GetGridType(cell.CellX(), cell.CellY()), this);
|
|
Balance();
|
|
return true;
|
|
//}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Map::LoadGrid(float x, float y)
|
|
{
|
|
EnsureGridLoaded(Cell(x, y));
|
|
}
|
|
|
|
bool Map::AddPlayerToMap(Player* player)
|
|
{
|
|
CellCoord cellCoord = Trinity::ComputeCellCoord(player->GetPositionX(), player->GetPositionY());
|
|
if (!cellCoord.IsCoordValid())
|
|
{
|
|
sLog->outError("Map::Add: Player (GUID: %u) has invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord);
|
|
return false;
|
|
}
|
|
|
|
Cell cell(cellCoord);
|
|
EnsureGridLoaded(cell);
|
|
AddToGrid(player, cell);
|
|
|
|
// Check if we are adding to correct map
|
|
ASSERT (player->GetMap() == this);
|
|
player->SetMap(this);
|
|
player->AddToWorld();
|
|
|
|
SendInitTransports(player);
|
|
SendInitSelf(player);
|
|
SendZoneDynamicInfo(player);
|
|
|
|
player->m_clientGUIDs.clear();
|
|
player->UpdateObjectVisibility(false);
|
|
|
|
sScriptMgr->OnPlayerEnterMap(this, player);
|
|
return true;
|
|
}
|
|
|
|
template<class T>
|
|
void Map::InitializeObject(T* /*obj*/)
|
|
{
|
|
}
|
|
|
|
template<>
|
|
void Map::InitializeObject(Creature* /*obj*/)
|
|
{
|
|
//obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; // pussywizard: this is shit
|
|
}
|
|
|
|
template<>
|
|
void Map::InitializeObject(GameObject* /*obj*/)
|
|
{
|
|
//obj->_moveState = MAP_OBJECT_CELL_MOVE_NONE; // pussywizard: this is shit
|
|
}
|
|
|
|
template<class T>
|
|
bool Map::AddToMap(T* obj, bool checkTransport)
|
|
{
|
|
//TODO: Needs clean up. An object should not be added to map twice.
|
|
if (obj->IsInWorld())
|
|
{
|
|
ASSERT(obj->IsInGrid());
|
|
obj->UpdateObjectVisibility(true);
|
|
return true;
|
|
}
|
|
|
|
CellCoord cellCoord = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
|
|
//It will create many problems (including crashes) if an object is not added to grid after creation
|
|
//The correct way to fix it is to make AddToMap return false and delete the object if it is not added to grid
|
|
//But now AddToMap is used in too many places, I will just see how many ASSERT failures it will cause
|
|
ASSERT(cellCoord.IsCoordValid());
|
|
if (!cellCoord.IsCoordValid())
|
|
{
|
|
sLog->outError("Map::Add: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord);
|
|
return false; //Should delete object
|
|
}
|
|
|
|
Cell cell(cellCoord);
|
|
if (obj->isActiveObject())
|
|
EnsureGridLoaded(cell);
|
|
else
|
|
EnsureGridCreated(GridCoord(cell.GridX(), cell.GridY()));
|
|
|
|
AddToGrid(obj, cell);
|
|
|
|
//Must already be set before AddToMap. Usually during obj->Create.
|
|
//obj->SetMap(this);
|
|
obj->AddToWorld();
|
|
|
|
if (checkTransport)
|
|
if (!(obj->GetTypeId() == TYPEID_GAMEOBJECT && obj->ToGameObject()->IsTransport())) // dont add transport to transport ;d
|
|
if (Transport* transport = GetTransportForPos(obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj))
|
|
transport->AddPassenger(obj, true);
|
|
|
|
InitializeObject(obj);
|
|
|
|
if (obj->isActiveObject())
|
|
AddToActive(obj);
|
|
|
|
//something, such as vehicle, needs to be update immediately
|
|
//also, trigger needs to cast spell, if not update, cannot see visual
|
|
obj->UpdateObjectVisibility(true);
|
|
|
|
// Xinef: little hack for vehicles, accessories have to be added after visibility update so they wont fall off the vehicle, moved from Creature::AIM_Initialize
|
|
// Initialize vehicle, this is done only for summoned npcs, DB creatures are handled by grid loaders
|
|
if (obj->GetTypeId() == TYPEID_UNIT)
|
|
if (Vehicle* vehicle = obj->ToCreature()->GetVehicleKit())
|
|
vehicle->Reset();
|
|
return true;
|
|
}
|
|
|
|
template<>
|
|
bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/)
|
|
{
|
|
//TODO: Needs clean up. An object should not be added to map twice.
|
|
if (obj->IsInWorld())
|
|
return true;
|
|
|
|
CellCoord cellCoord = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY());
|
|
if (!cellCoord.IsCoordValid())
|
|
{
|
|
sLog->outError("Map::Add: Object " UI64FMTD " has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), cellCoord.x_coord, cellCoord.y_coord);
|
|
return false; //Should delete object
|
|
}
|
|
|
|
Cell cell(cellCoord);
|
|
if (obj->isActiveObject())
|
|
EnsureGridLoaded(cell);
|
|
|
|
obj->AddToWorld();
|
|
|
|
if (obj->isActiveObject())
|
|
AddToActive(obj);
|
|
|
|
_transports.insert(obj);
|
|
|
|
// Broadcast creation to players
|
|
if (!GetPlayers().isEmpty())
|
|
{
|
|
for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
|
|
{
|
|
if (itr->GetSource()->GetTransport() != obj)
|
|
{
|
|
UpdateData data;
|
|
obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource());
|
|
WorldPacket packet;
|
|
data.BuildPacket(&packet);
|
|
itr->GetSource()->SendDirectMessage(&packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Map::IsGridLoaded(const GridCoord &p) const
|
|
{
|
|
return (getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord));
|
|
}
|
|
|
|
void Map::VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<Trinity::ObjectUpdater, GridTypeMapContainer> &gridVisitor, TypeContainerVisitor<Trinity::ObjectUpdater, WorldTypeMapContainer> &worldVisitor)
|
|
{
|
|
// Check for valid position
|
|
if (!obj->IsPositionValid())
|
|
return;
|
|
|
|
if (obj->GetGridActivationRange() <= 0.0f) // pussywizard: gameobjects for example are on active lists, but range is equal to 0 (they just prevent grid unloading)
|
|
return;
|
|
|
|
// Update mobs/objects in ALL visible cells around object!
|
|
CellArea area = Cell::CalculateCellArea(obj->GetPositionX(), obj->GetPositionY(), obj->GetGridActivationRange());
|
|
|
|
for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
|
|
{
|
|
for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
|
|
{
|
|
// marked cells are those that have been visited
|
|
// don't visit the same cell twice
|
|
uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
|
|
if (isCellMarked(cell_id))
|
|
continue;
|
|
|
|
markCell(cell_id);
|
|
CellCoord pair(x, y);
|
|
Cell cell(pair);
|
|
//cell.SetNoCreate(); // in mmaps this is missing
|
|
|
|
Visit(cell, gridVisitor);
|
|
Visit(cell, worldVisitor);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
|
{
|
|
uint32 mapId = GetId(); // pussywizard: for crashlogs
|
|
sLog->outDebug(LOG_FILTER_POOLSYS, "%u", mapId); // pussywizard: for crashlogs
|
|
|
|
if (t_diff)
|
|
_dynamicTree.update(t_diff);
|
|
/// update worldsessions for existing players
|
|
for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
|
|
{
|
|
Player* player = m_mapRefIter->GetSource();
|
|
if (player && player->IsInWorld())
|
|
{
|
|
//player->Update(t_diff);
|
|
WorldSession* session = player->GetSession();
|
|
MapSessionFilter updater(session);
|
|
session->Update(s_diff, updater);
|
|
}
|
|
}
|
|
|
|
if (!t_diff)
|
|
{
|
|
for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
|
|
{
|
|
Player* player = m_mapRefIter->GetSource();
|
|
|
|
if (!player || !player->IsInWorld())
|
|
continue;
|
|
|
|
// update players at tick
|
|
player->Update(s_diff);
|
|
}
|
|
|
|
HandleDelayedVisibility();
|
|
return;
|
|
}
|
|
|
|
/// update active cells around players and active objects
|
|
resetMarkedCells();
|
|
|
|
Trinity::ObjectUpdater updater(t_diff);
|
|
// for creature
|
|
TypeContainerVisitor<Trinity::ObjectUpdater, GridTypeMapContainer > grid_object_update(updater);
|
|
// for pets
|
|
TypeContainerVisitor<Trinity::ObjectUpdater, WorldTypeMapContainer > world_object_update(updater);
|
|
|
|
// pussywizard: container for far creatures in combat with players
|
|
std::vector<Creature*> updateList; updateList.reserve(10);
|
|
|
|
// the player iterator is stored in the map object
|
|
// to make sure calls to Map::Remove don't invalidate it
|
|
for (m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
|
|
{
|
|
Player* player = m_mapRefIter->GetSource();
|
|
|
|
if (!player || !player->IsInWorld())
|
|
continue;
|
|
|
|
// update players at tick
|
|
player->Update(s_diff);
|
|
|
|
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
|
|
|
|
// handle updates for creatures in combat with player and are more than X yards away
|
|
if (player->IsInCombat())
|
|
{
|
|
updateList.clear();
|
|
float rangeSq = player->GetGridActivationRange() - 1.0f; rangeSq = rangeSq*rangeSq;
|
|
HostileReference* ref = player->getHostileRefManager().getFirst();
|
|
while (ref)
|
|
{
|
|
if (Unit* unit = ref->GetSource()->GetOwner())
|
|
if (Creature* cre = unit->ToCreature())
|
|
if (cre->FindMap() == player->FindMap() && cre->GetExactDist2dSq(player) > rangeSq)
|
|
updateList.push_back(cre);
|
|
ref = ref->next();
|
|
}
|
|
for (std::vector<Creature*>::const_iterator itr = updateList.begin(); itr != updateList.end(); ++itr)
|
|
VisitNearbyCellsOf(*itr, grid_object_update, world_object_update);
|
|
}
|
|
}
|
|
|
|
// non-player active objects, increasing iterator in the loop in case of object removal
|
|
for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end();)
|
|
{
|
|
WorldObject* obj = *m_activeNonPlayersIter;
|
|
++m_activeNonPlayersIter;
|
|
|
|
if (!obj || !obj->IsInWorld())
|
|
continue;
|
|
|
|
VisitNearbyCellsOf(obj, grid_object_update, world_object_update);
|
|
}
|
|
|
|
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) // pussywizard: transports updated after VisitNearbyCellsOf, grids around are loaded, everything ok
|
|
{
|
|
MotionTransport* transport = *_transportsUpdateIter;
|
|
++_transportsUpdateIter;
|
|
|
|
if (!transport->IsInWorld())
|
|
continue;
|
|
|
|
transport->Update(t_diff);
|
|
}
|
|
|
|
///- Process necessary scripts
|
|
if (!m_scriptSchedule.empty())
|
|
{
|
|
i_scriptLock = true;
|
|
ScriptsProcess();
|
|
i_scriptLock = false;
|
|
}
|
|
|
|
MoveAllCreaturesInMoveList();
|
|
MoveAllGameObjectsInMoveList();
|
|
MoveAllDynamicObjectsInMoveList();
|
|
|
|
HandleDelayedVisibility();
|
|
|
|
sScriptMgr->OnMapUpdate(this, t_diff);
|
|
|
|
BuildAndSendUpdateForObjects(); // pussywizard
|
|
|
|
sLog->outDebug(LOG_FILTER_POOLSYS, "%u", mapId); // pussywizard: for crashlogs
|
|
}
|
|
|
|
void Map::HandleDelayedVisibility()
|
|
{
|
|
if (i_objectsForDelayedVisibility.empty())
|
|
return;
|
|
for (UNORDERED_SET<Unit*>::iterator itr = i_objectsForDelayedVisibility.begin(); itr != i_objectsForDelayedVisibility.end(); ++itr)
|
|
(*itr)->ExecuteDelayedUnitRelocationEvent();
|
|
i_objectsForDelayedVisibility.clear();
|
|
}
|
|
|
|
struct ResetNotifier
|
|
{
|
|
template<class T>inline void resetNotify(GridRefManager<T> &m)
|
|
{
|
|
for (typename GridRefManager<T>::iterator iter=m.begin(); iter != m.end(); ++iter)
|
|
iter->GetSource()->ResetAllNotifies();
|
|
}
|
|
template<class T> void Visit(GridRefManager<T> &) {}
|
|
void Visit(CreatureMapType &m) { resetNotify<Creature>(m);}
|
|
void Visit(PlayerMapType &m) { resetNotify<Player>(m);}
|
|
};
|
|
|
|
void Map::RemovePlayerFromMap(Player* player, bool remove)
|
|
{
|
|
player->getHostileRefManager().deleteReferences(); // pussywizard: multithreading crashfix
|
|
|
|
bool inWorld = player->IsInWorld();
|
|
player->RemoveFromWorld();
|
|
SendRemoveTransports(player);
|
|
|
|
if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
|
|
player->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility(true)
|
|
|
|
if (player->IsInGrid())
|
|
player->RemoveFromGrid();
|
|
else
|
|
ASSERT(remove); //maybe deleted in logoutplayer when player is not in a map
|
|
|
|
if (remove)
|
|
{
|
|
sScriptMgr->OnPlayerLeaveMap(this, player);
|
|
DeleteFromWorld(player);
|
|
}
|
|
}
|
|
|
|
void Map::AfterPlayerUnlinkFromMap()
|
|
{
|
|
}
|
|
|
|
template<class T>
|
|
void Map::RemoveFromMap(T *obj, bool remove)
|
|
{
|
|
bool inWorld = obj->IsInWorld() && obj->GetTypeId() >= TYPEID_UNIT && obj->GetTypeId() <= TYPEID_GAMEOBJECT;
|
|
obj->RemoveFromWorld();
|
|
|
|
if (obj->isActiveObject())
|
|
RemoveFromActive(obj);
|
|
|
|
if (!inWorld) // pussywizard: if was in world, RemoveFromWorld() called DestroyForNearbyPlayers()
|
|
obj->DestroyForNearbyPlayers(); // pussywizard: previous player->UpdateObjectVisibility()
|
|
|
|
obj->RemoveFromGrid();
|
|
|
|
obj->ResetMap();
|
|
|
|
if (remove)
|
|
DeleteFromWorld(obj);
|
|
}
|
|
|
|
template<>
|
|
void Map::RemoveFromMap(MotionTransport* obj, bool remove)
|
|
{
|
|
obj->RemoveFromWorld();
|
|
if (obj->isActiveObject())
|
|
RemoveFromActive(obj);
|
|
|
|
Map::PlayerList const& players = GetPlayers();
|
|
if (!players.isEmpty())
|
|
{
|
|
UpdateData data;
|
|
obj->BuildOutOfRangeUpdateBlock(&data);
|
|
WorldPacket packet;
|
|
data.BuildPacket(&packet);
|
|
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
|
if (itr->GetSource()->GetTransport() != obj)
|
|
itr->GetSource()->SendDirectMessage(&packet);
|
|
}
|
|
|
|
if (_transportsUpdateIter != _transports.end())
|
|
{
|
|
TransportsContainer::iterator itr = _transports.find(obj);
|
|
if (itr == _transports.end())
|
|
return;
|
|
if (itr == _transportsUpdateIter)
|
|
++_transportsUpdateIter;
|
|
_transports.erase(itr);
|
|
}
|
|
else
|
|
_transports.erase(obj);
|
|
|
|
obj->ResetMap();
|
|
|
|
if (remove)
|
|
{
|
|
// if option set then object already saved at this moment
|
|
if (!sWorld->getBoolConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY))
|
|
obj->SaveRespawnTime();
|
|
DeleteFromWorld(obj);
|
|
}
|
|
}
|
|
|
|
void Map::PlayerRelocation(Player* player, float x, float y, float z, float o)
|
|
{
|
|
Cell old_cell(player->GetPositionX(), player->GetPositionY());
|
|
Cell new_cell(x, y);
|
|
|
|
if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell))
|
|
{
|
|
player->RemoveFromGrid();
|
|
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
|
|
AddToGrid(player, new_cell);
|
|
}
|
|
|
|
player->Relocate(x, y, z, o);
|
|
if (player->IsVehicle())
|
|
player->GetVehicleKit()->RelocatePassengers();
|
|
|
|
player->UpdateObjectVisibility(false);
|
|
}
|
|
|
|
void Map::CreatureRelocation(Creature* creature, float x, float y, float z, float o)
|
|
{
|
|
Cell old_cell = creature->GetCurrentCell();
|
|
Cell new_cell(x, y);
|
|
|
|
if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell))
|
|
{
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
|
|
AddCreatureToMoveList(creature);
|
|
}
|
|
else
|
|
RemoveCreatureFromMoveList(creature);
|
|
|
|
creature->Relocate(x, y, z, o);
|
|
if (creature->IsVehicle())
|
|
creature->GetVehicleKit()->RelocatePassengers();
|
|
|
|
creature->UpdateObjectVisibility(false);
|
|
}
|
|
|
|
void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float o)
|
|
{
|
|
Cell old_cell = go->GetCurrentCell();
|
|
Cell new_cell(x, y);
|
|
|
|
if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell))
|
|
{
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
|
|
AddGameObjectToMoveList(go);
|
|
}
|
|
else
|
|
RemoveGameObjectFromMoveList(go);
|
|
|
|
go->Relocate(x, y, z, o);
|
|
go->UpdateModelPosition();
|
|
|
|
go->UpdateObjectVisibility(false);
|
|
}
|
|
|
|
void Map::DynamicObjectRelocation(DynamicObject* dynObj, float x, float y, float z, float o)
|
|
{
|
|
Cell old_cell = dynObj->GetCurrentCell();
|
|
Cell new_cell(x, y);
|
|
|
|
if (old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell))
|
|
{
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
|
|
AddDynamicObjectToMoveList(dynObj);
|
|
}
|
|
else
|
|
RemoveDynamicObjectFromMoveList(dynObj);
|
|
|
|
dynObj->Relocate(x, y, z, o);
|
|
|
|
dynObj->UpdateObjectVisibility(false);
|
|
}
|
|
|
|
void Map::AddCreatureToMoveList(Creature* c)
|
|
{
|
|
if (c->_moveState == MAP_OBJECT_CELL_MOVE_NONE)
|
|
_creaturesToMove.push_back(c);
|
|
c->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE;
|
|
}
|
|
|
|
void Map::RemoveCreatureFromMoveList(Creature* c)
|
|
{
|
|
if (c->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE)
|
|
c->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
|
|
}
|
|
|
|
void Map::AddGameObjectToMoveList(GameObject* go)
|
|
{
|
|
if (go->_moveState == MAP_OBJECT_CELL_MOVE_NONE)
|
|
_gameObjectsToMove.push_back(go);
|
|
go->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE;
|
|
}
|
|
|
|
void Map::RemoveGameObjectFromMoveList(GameObject* go)
|
|
{
|
|
if (go->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE)
|
|
go->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
|
|
}
|
|
|
|
void Map::AddDynamicObjectToMoveList(DynamicObject* dynObj)
|
|
{
|
|
if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_NONE)
|
|
_dynamicObjectsToMove.push_back(dynObj);
|
|
dynObj->_moveState = MAP_OBJECT_CELL_MOVE_ACTIVE;
|
|
}
|
|
|
|
void Map::RemoveDynamicObjectFromMoveList(DynamicObject* dynObj)
|
|
{
|
|
if (dynObj->_moveState == MAP_OBJECT_CELL_MOVE_ACTIVE)
|
|
dynObj->_moveState = MAP_OBJECT_CELL_MOVE_INACTIVE;
|
|
}
|
|
|
|
void Map::MoveAllCreaturesInMoveList()
|
|
{
|
|
for (std::vector<Creature*>::iterator itr = _creaturesToMove.begin(); itr != _creaturesToMove.end(); ++itr)
|
|
{
|
|
Creature* c = *itr;
|
|
if (c->FindMap() != this)
|
|
continue;
|
|
|
|
if (c->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE)
|
|
{
|
|
c->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
|
|
continue;
|
|
}
|
|
|
|
c->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
|
|
if (!c->IsInWorld())
|
|
continue;
|
|
|
|
Cell const& old_cell = c->GetCurrentCell();
|
|
Cell new_cell(c->GetPositionX(), c->GetPositionY());
|
|
|
|
c->RemoveFromGrid();
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
AddToGrid(c, new_cell);
|
|
}
|
|
_creaturesToMove.clear();
|
|
}
|
|
|
|
void Map::MoveAllGameObjectsInMoveList()
|
|
{
|
|
for (std::vector<GameObject*>::iterator itr = _gameObjectsToMove.begin(); itr != _gameObjectsToMove.end(); ++itr)
|
|
{
|
|
GameObject* go = *itr;
|
|
if (go->FindMap() != this)
|
|
continue;
|
|
|
|
if (go->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE)
|
|
{
|
|
go->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
|
|
continue;
|
|
}
|
|
|
|
go->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
|
|
if (!go->IsInWorld())
|
|
continue;
|
|
|
|
Cell const& old_cell = go->GetCurrentCell();
|
|
Cell new_cell(go->GetPositionX(), go->GetPositionY());
|
|
|
|
go->RemoveFromGrid();
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
AddToGrid(go, new_cell);
|
|
}
|
|
_gameObjectsToMove.clear();
|
|
}
|
|
|
|
void Map::MoveAllDynamicObjectsInMoveList()
|
|
{
|
|
for (std::vector<DynamicObject*>::iterator itr = _dynamicObjectsToMove.begin(); itr != _dynamicObjectsToMove.end(); ++itr)
|
|
{
|
|
DynamicObject* dynObj = *itr;
|
|
if (dynObj->FindMap() != this)
|
|
continue;
|
|
|
|
if (dynObj->_moveState != MAP_OBJECT_CELL_MOVE_ACTIVE)
|
|
{
|
|
dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
|
|
continue;
|
|
}
|
|
|
|
dynObj->_moveState = MAP_OBJECT_CELL_MOVE_NONE;
|
|
if (!dynObj->IsInWorld())
|
|
continue;
|
|
|
|
Cell const& old_cell = dynObj->GetCurrentCell();
|
|
Cell new_cell(dynObj->GetPositionX(), dynObj->GetPositionY());
|
|
|
|
dynObj->RemoveFromGrid();
|
|
if (old_cell.DiffGrid(new_cell))
|
|
EnsureGridLoaded(new_cell);
|
|
AddToGrid(dynObj, new_cell);
|
|
}
|
|
_dynamicObjectsToMove.clear();
|
|
}
|
|
|
|
bool Map::UnloadGrid(NGridType& ngrid)
|
|
{
|
|
// pussywizard: UnloadGrid only done when whole map is unloaded, no need to worry about moving npcs between grids, etc.
|
|
|
|
const uint32 x = ngrid.getX();
|
|
const uint32 y = ngrid.getY();
|
|
|
|
{
|
|
ObjectGridCleaner worker;
|
|
TypeContainerVisitor<ObjectGridCleaner, GridTypeMapContainer> visitor(worker);
|
|
ngrid.VisitAllGrids(visitor);
|
|
}
|
|
|
|
RemoveAllObjectsInRemoveList();
|
|
|
|
{
|
|
ObjectGridUnloader worker;
|
|
TypeContainerVisitor<ObjectGridUnloader, GridTypeMapContainer> visitor(worker);
|
|
ngrid.VisitAllGrids(visitor);
|
|
}
|
|
|
|
ASSERT(i_objectsToRemove.empty());
|
|
|
|
delete &ngrid;
|
|
setNGrid(NULL, x, y);
|
|
|
|
int gx = (MAX_NUMBER_OF_GRIDS - 1) - x;
|
|
int gy = (MAX_NUMBER_OF_GRIDS - 1) - y;
|
|
|
|
if (i_InstanceId == 0)
|
|
{
|
|
if (GridMaps[gx][gy])
|
|
{
|
|
GridMaps[gx][gy]->unloadData();
|
|
delete GridMaps[gx][gy];
|
|
}
|
|
// x and y are swapped
|
|
VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy);
|
|
MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy);
|
|
}
|
|
|
|
GridMaps[gx][gy] = NULL;
|
|
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outStaticDebug("Unloading grid[%u, %u] for map %u finished", x, y, GetId());
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
void Map::RemoveAllPlayers()
|
|
{
|
|
if (HavePlayers())
|
|
{
|
|
for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
{
|
|
Player* player = itr->GetSource();
|
|
if (!player->IsBeingTeleportedFar())
|
|
{
|
|
// this is happening for bg
|
|
sLog->outError("Map::UnloadAll: player %s is still in map %u during unload, this should not happen!", player->GetName().c_str(), GetId());
|
|
player->TeleportTo(player->m_homebindMapId, player->m_homebindX, player->m_homebindY, player->m_homebindZ, player->GetOrientation());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Map::UnloadAll()
|
|
{
|
|
// clear all delayed moves, useless anyway do this moves before map unload.
|
|
_creaturesToMove.clear();
|
|
_gameObjectsToMove.clear();
|
|
|
|
for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end();)
|
|
{
|
|
NGridType &grid(*i->GetSource());
|
|
++i;
|
|
UnloadGrid(grid); // deletes the grid and removes it from the GridRefManager
|
|
}
|
|
|
|
// pussywizard: crashfix, some npc can be left on transport (not a default passenger)
|
|
if (!AllTransportsEmpty())
|
|
AllTransportsRemovePassengers();
|
|
|
|
for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();)
|
|
{
|
|
MotionTransport* transport = *itr;
|
|
++itr;
|
|
|
|
transport->RemoveFromWorld();
|
|
delete transport;
|
|
}
|
|
|
|
_transports.clear();
|
|
}
|
|
|
|
// *****************************
|
|
// Grid function
|
|
// *****************************
|
|
GridMap::GridMap()
|
|
{
|
|
_flags = 0;
|
|
// Area data
|
|
_gridArea = 0;
|
|
_areaMap = nullptr;
|
|
// Height level data
|
|
_gridHeight = INVALID_HEIGHT;
|
|
_gridGetHeight = &GridMap::getHeightFromFlat;
|
|
_gridIntHeightMultiplier = 0;
|
|
m_V9 = nullptr;
|
|
m_V8 = nullptr;
|
|
_maxHeight = nullptr;
|
|
_minHeight = nullptr;
|
|
// Liquid data
|
|
_liquidType = 0;
|
|
_liquidOffX = 0;
|
|
_liquidOffY = 0;
|
|
_liquidWidth = 0;
|
|
_liquidHeight = 0;
|
|
_liquidLevel = INVALID_HEIGHT;
|
|
_liquidEntry = nullptr;
|
|
_liquidFlags = nullptr;
|
|
_liquidMap = nullptr;
|
|
}
|
|
|
|
GridMap::~GridMap()
|
|
{
|
|
unloadData();
|
|
}
|
|
|
|
bool GridMap::loadData(char *filename)
|
|
{
|
|
// Unload old data if exist
|
|
unloadData();
|
|
|
|
map_fileheader header;
|
|
// Not return error if file not found
|
|
FILE* in = fopen(filename, "rb");
|
|
if (!in)
|
|
return true;
|
|
|
|
if (fread(&header, sizeof(header), 1, in) != 1)
|
|
{
|
|
fclose(in);
|
|
return false;
|
|
}
|
|
|
|
if (header.mapMagic == MapMagic.asUInt && header.versionMagic == MapVersionMagic.asUInt)
|
|
{
|
|
// loadup area data
|
|
if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize))
|
|
{
|
|
sLog->outError("Error loading map area data\n");
|
|
fclose(in);
|
|
return false;
|
|
}
|
|
// loadup height data
|
|
if (header.heightMapOffset && !loadHeightData(in, header.heightMapOffset, header.heightMapSize))
|
|
{
|
|
sLog->outError("Error loading map height data\n");
|
|
fclose(in);
|
|
return false;
|
|
}
|
|
// loadup liquid data
|
|
if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize))
|
|
{
|
|
sLog->outError("Error loading map liquids data\n");
|
|
fclose(in);
|
|
return false;
|
|
}
|
|
fclose(in);
|
|
return true;
|
|
}
|
|
sLog->outError("Map file '%s' is from an incompatible clientversion. Please recreate using the mapextractor.", filename);
|
|
fclose(in);
|
|
return false;
|
|
}
|
|
|
|
void GridMap::unloadData()
|
|
{
|
|
delete[] _areaMap;
|
|
delete[] m_V9;
|
|
delete[] m_V8;
|
|
delete[] _maxHeight;
|
|
delete[] _minHeight;
|
|
delete[] _liquidEntry;
|
|
delete[] _liquidFlags;
|
|
delete[] _liquidMap;
|
|
_areaMap = nullptr;
|
|
m_V9 = nullptr;
|
|
m_V8 = nullptr;
|
|
_maxHeight = nullptr;
|
|
_minHeight = nullptr;
|
|
_liquidEntry = nullptr;
|
|
_liquidFlags = nullptr;
|
|
_liquidMap = nullptr;
|
|
_gridGetHeight = &GridMap::getHeightFromFlat;
|
|
}
|
|
|
|
bool GridMap::loadAreaData(FILE* in, uint32 offset, uint32 /*size*/)
|
|
{
|
|
map_areaHeader header;
|
|
fseek(in, offset, SEEK_SET);
|
|
|
|
if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapAreaMagic.asUInt)
|
|
return false;
|
|
|
|
_gridArea = header.gridArea;
|
|
if (!(header.flags & MAP_AREA_NO_AREA))
|
|
{
|
|
_areaMap = new uint16 [16*16];
|
|
if (fread(_areaMap, sizeof(uint16), 16*16, in) != 16*16)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/)
|
|
{
|
|
map_heightHeader header;
|
|
fseek(in, offset, SEEK_SET);
|
|
|
|
if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapHeightMagic.asUInt)
|
|
return false;
|
|
|
|
_gridHeight = header.gridHeight;
|
|
if (!(header.flags & MAP_HEIGHT_NO_HEIGHT))
|
|
{
|
|
if ((header.flags & MAP_HEIGHT_AS_INT16))
|
|
{
|
|
m_uint16_V9 = new uint16 [129*129];
|
|
m_uint16_V8 = new uint16 [128*128];
|
|
if (fread(m_uint16_V9, sizeof(uint16), 129*129, in) != 129*129 ||
|
|
fread(m_uint16_V8, sizeof(uint16), 128*128, in) != 128*128)
|
|
return false;
|
|
_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535;
|
|
_gridGetHeight = &GridMap::getHeightFromUint16;
|
|
}
|
|
else if ((header.flags & MAP_HEIGHT_AS_INT8))
|
|
{
|
|
m_uint8_V9 = new uint8 [129*129];
|
|
m_uint8_V8 = new uint8 [128*128];
|
|
if (fread(m_uint8_V9, sizeof(uint8), 129*129, in) != 129*129 ||
|
|
fread(m_uint8_V8, sizeof(uint8), 128*128, in) != 128*128)
|
|
return false;
|
|
_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 255;
|
|
_gridGetHeight = &GridMap::getHeightFromUint8;
|
|
}
|
|
else
|
|
{
|
|
m_V9 = new float [129*129];
|
|
m_V8 = new float [128*128];
|
|
if (fread(m_V9, sizeof(float), 129*129, in) != 129*129 ||
|
|
fread(m_V8, sizeof(float), 128*128, in) != 128*128)
|
|
return false;
|
|
_gridGetHeight = &GridMap::getHeightFromFloat;
|
|
}
|
|
}
|
|
else
|
|
_gridGetHeight = &GridMap::getHeightFromFlat;
|
|
|
|
if (header.flags & MAP_HEIGHT_HAS_FLIGHT_BOUNDS)
|
|
{
|
|
_maxHeight = new int16[3 * 3];
|
|
_minHeight = new int16[3 * 3];
|
|
if (fread(_maxHeight, sizeof(int16), 3 * 3, in) != 3 * 3 ||
|
|
fread(_minHeight, sizeof(int16), 3 * 3, in) != 3 * 3)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GridMap::loadLiquidData(FILE* in, uint32 offset, uint32 /*size*/)
|
|
{
|
|
map_liquidHeader header;
|
|
fseek(in, offset, SEEK_SET);
|
|
|
|
if (fread(&header, sizeof(header), 1, in) != 1 || header.fourcc != MapLiquidMagic.asUInt)
|
|
return false;
|
|
|
|
_liquidType = header.liquidType;
|
|
_liquidOffX = header.offsetX;
|
|
_liquidOffY = header.offsetY;
|
|
_liquidWidth = header.width;
|
|
_liquidHeight = header.height;
|
|
_liquidLevel = header.liquidLevel;
|
|
|
|
if (!(header.flags & MAP_LIQUID_NO_TYPE))
|
|
{
|
|
_liquidEntry = new uint16[16*16];
|
|
if (fread(_liquidEntry, sizeof(uint16), 16*16, in) != 16*16)
|
|
return false;
|
|
|
|
_liquidFlags = new uint8[16*16];
|
|
if (fread(_liquidFlags, sizeof(uint8), 16*16, in) != 16*16)
|
|
return false;
|
|
}
|
|
if (!(header.flags & MAP_LIQUID_NO_HEIGHT))
|
|
{
|
|
_liquidMap = new float[uint32(_liquidWidth) * uint32(_liquidHeight)];
|
|
if (fread(_liquidMap, sizeof(float), _liquidWidth*_liquidHeight, in) != (uint32(_liquidWidth) * uint32(_liquidHeight)))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint16 GridMap::getArea(float x, float y) const
|
|
{
|
|
if (!_areaMap)
|
|
return _gridArea;
|
|
|
|
x = 16 * (32 - x/SIZE_OF_GRIDS);
|
|
y = 16 * (32 - y/SIZE_OF_GRIDS);
|
|
int lx = (int)x & 15;
|
|
int ly = (int)y & 15;
|
|
return _areaMap[lx*16 + ly];
|
|
}
|
|
|
|
float GridMap::getHeightFromFlat(float /*x*/, float /*y*/) const
|
|
{
|
|
return _gridHeight;
|
|
}
|
|
|
|
float GridMap::getHeightFromFloat(float x, float y) const
|
|
{
|
|
if (!m_V8 || !m_V9)
|
|
return _gridHeight;
|
|
|
|
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
|
|
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
|
|
|
|
int x_int = (int)x;
|
|
int y_int = (int)y;
|
|
x -= x_int;
|
|
y -= y_int;
|
|
x_int&=(MAP_RESOLUTION - 1);
|
|
y_int&=(MAP_RESOLUTION - 1);
|
|
|
|
// Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid
|
|
// +--------------> X
|
|
// | h1-------h2 Coordinates is:
|
|
// | | \ 1 / | h1 0, 0
|
|
// | | \ / | h2 0, 1
|
|
// | | 2 h5 3 | h3 1, 0
|
|
// | | / \ | h4 1, 1
|
|
// | | / 4 \ | h5 1/2, 1/2
|
|
// | h3-------h4
|
|
// V Y
|
|
// For find height need
|
|
// 1 - detect triangle
|
|
// 2 - solve linear equation from triangle points
|
|
// Calculate coefficients for solve h = a*x + b*y + c
|
|
|
|
float a, b, c;
|
|
// Select triangle:
|
|
if (x+y < 1)
|
|
{
|
|
if (x > y)
|
|
{
|
|
// 1 triangle (h1, h2, h5 points)
|
|
float h1 = m_V9[(x_int)*129 + y_int];
|
|
float h2 = m_V9[(x_int+1)*129 + y_int];
|
|
float h5 = 2 * m_V8[x_int*128 + y_int];
|
|
a = h2-h1;
|
|
b = h5-h1-h2;
|
|
c = h1;
|
|
}
|
|
else
|
|
{
|
|
// 2 triangle (h1, h3, h5 points)
|
|
float h1 = m_V9[x_int*129 + y_int ];
|
|
float h3 = m_V9[x_int*129 + y_int+1];
|
|
float h5 = 2 * m_V8[x_int*128 + y_int];
|
|
a = h5 - h1 - h3;
|
|
b = h3 - h1;
|
|
c = h1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (x > y)
|
|
{
|
|
// 3 triangle (h2, h4, h5 points)
|
|
float h2 = m_V9[(x_int+1)*129 + y_int ];
|
|
float h4 = m_V9[(x_int+1)*129 + y_int+1];
|
|
float h5 = 2 * m_V8[x_int*128 + y_int];
|
|
a = h2 + h4 - h5;
|
|
b = h4 - h2;
|
|
c = h5 - h4;
|
|
}
|
|
else
|
|
{
|
|
// 4 triangle (h3, h4, h5 points)
|
|
float h3 = m_V9[(x_int)*129 + y_int+1];
|
|
float h4 = m_V9[(x_int+1)*129 + y_int+1];
|
|
float h5 = 2 * m_V8[x_int*128 + y_int];
|
|
a = h4 - h3;
|
|
b = h3 + h4 - h5;
|
|
c = h5 - h4;
|
|
}
|
|
}
|
|
// Calculate height
|
|
return a * x + b * y + c;
|
|
}
|
|
|
|
float GridMap::getHeightFromUint8(float x, float y) const
|
|
{
|
|
if (!m_uint8_V8 || !m_uint8_V9)
|
|
return _gridHeight;
|
|
|
|
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
|
|
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
|
|
|
|
int x_int = (int)x;
|
|
int y_int = (int)y;
|
|
x -= x_int;
|
|
y -= y_int;
|
|
x_int&=(MAP_RESOLUTION - 1);
|
|
y_int&=(MAP_RESOLUTION - 1);
|
|
|
|
int32 a, b, c;
|
|
uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int];
|
|
if (x+y < 1)
|
|
{
|
|
if (x > y)
|
|
{
|
|
// 1 triangle (h1, h2, h5 points)
|
|
int32 h1 = V9_h1_ptr[ 0];
|
|
int32 h2 = V9_h1_ptr[129];
|
|
int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
|
|
a = h2-h1;
|
|
b = h5-h1-h2;
|
|
c = h1;
|
|
}
|
|
else
|
|
{
|
|
// 2 triangle (h1, h3, h5 points)
|
|
int32 h1 = V9_h1_ptr[0];
|
|
int32 h3 = V9_h1_ptr[1];
|
|
int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
|
|
a = h5 - h1 - h3;
|
|
b = h3 - h1;
|
|
c = h1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (x > y)
|
|
{
|
|
// 3 triangle (h2, h4, h5 points)
|
|
int32 h2 = V9_h1_ptr[129];
|
|
int32 h4 = V9_h1_ptr[130];
|
|
int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
|
|
a = h2 + h4 - h5;
|
|
b = h4 - h2;
|
|
c = h5 - h4;
|
|
}
|
|
else
|
|
{
|
|
// 4 triangle (h3, h4, h5 points)
|
|
int32 h3 = V9_h1_ptr[ 1];
|
|
int32 h4 = V9_h1_ptr[130];
|
|
int32 h5 = 2 * m_uint8_V8[x_int*128 + y_int];
|
|
a = h4 - h3;
|
|
b = h3 + h4 - h5;
|
|
c = h5 - h4;
|
|
}
|
|
}
|
|
// Calculate height
|
|
return (float)((a * x) + (b * y) + c)*_gridIntHeightMultiplier + _gridHeight;
|
|
}
|
|
|
|
float GridMap::getHeightFromUint16(float x, float y) const
|
|
{
|
|
if (!m_uint16_V8 || !m_uint16_V9)
|
|
return _gridHeight;
|
|
|
|
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
|
|
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
|
|
|
|
int x_int = (int)x;
|
|
int y_int = (int)y;
|
|
x -= x_int;
|
|
y -= y_int;
|
|
x_int&=(MAP_RESOLUTION - 1);
|
|
y_int&=(MAP_RESOLUTION - 1);
|
|
|
|
int32 a, b, c;
|
|
uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int];
|
|
if (x+y < 1)
|
|
{
|
|
if (x > y)
|
|
{
|
|
// 1 triangle (h1, h2, h5 points)
|
|
int32 h1 = V9_h1_ptr[ 0];
|
|
int32 h2 = V9_h1_ptr[129];
|
|
int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
|
|
a = h2-h1;
|
|
b = h5-h1-h2;
|
|
c = h1;
|
|
}
|
|
else
|
|
{
|
|
// 2 triangle (h1, h3, h5 points)
|
|
int32 h1 = V9_h1_ptr[0];
|
|
int32 h3 = V9_h1_ptr[1];
|
|
int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
|
|
a = h5 - h1 - h3;
|
|
b = h3 - h1;
|
|
c = h1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (x > y)
|
|
{
|
|
// 3 triangle (h2, h4, h5 points)
|
|
int32 h2 = V9_h1_ptr[129];
|
|
int32 h4 = V9_h1_ptr[130];
|
|
int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
|
|
a = h2 + h4 - h5;
|
|
b = h4 - h2;
|
|
c = h5 - h4;
|
|
}
|
|
else
|
|
{
|
|
// 4 triangle (h3, h4, h5 points)
|
|
int32 h3 = V9_h1_ptr[ 1];
|
|
int32 h4 = V9_h1_ptr[130];
|
|
int32 h5 = 2 * m_uint16_V8[x_int*128 + y_int];
|
|
a = h4 - h3;
|
|
b = h3 + h4 - h5;
|
|
c = h5 - h4;
|
|
}
|
|
}
|
|
// Calculate height
|
|
return (float)((a * x) + (b * y) + c)*_gridIntHeightMultiplier + _gridHeight;
|
|
}
|
|
|
|
float GridMap::getMinHeight(float x, float y) const
|
|
{
|
|
if (!_minHeight)
|
|
return -500.0f;
|
|
|
|
static uint32 const indices[] =
|
|
{
|
|
3, 0, 4,
|
|
0, 1, 4,
|
|
1, 2, 4,
|
|
2, 5, 4,
|
|
5, 8, 4,
|
|
8, 7, 4,
|
|
7, 6, 4,
|
|
6, 3, 4
|
|
};
|
|
|
|
static float const boundGridCoords[] =
|
|
{
|
|
0.0f, 0.0f,
|
|
0.0f, -266.66666f,
|
|
0.0f, -533.33331f,
|
|
-266.66666f, 0.0f,
|
|
-266.66666f, -266.66666f,
|
|
-266.66666f, -533.33331f,
|
|
-533.33331f, 0.0f,
|
|
-533.33331f, -266.66666f,
|
|
-533.33331f, -533.33331f
|
|
};
|
|
|
|
Cell cell(x, y);
|
|
float gx = x - (int32(cell.GridX()) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS;
|
|
float gy = y - (int32(cell.GridY()) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS;
|
|
|
|
uint32 quarterIndex = 0;
|
|
if (cell.CellY() < MAX_NUMBER_OF_CELLS / 2)
|
|
{
|
|
if (cell.CellX() < MAX_NUMBER_OF_CELLS / 2)
|
|
{
|
|
quarterIndex = 4 + (gy > gx);
|
|
}
|
|
else
|
|
quarterIndex = 2 + ((-SIZE_OF_GRIDS - gx) > gy);
|
|
}
|
|
else if (cell.CellX() < MAX_NUMBER_OF_CELLS / 2)
|
|
{
|
|
quarterIndex = 6 + ((-SIZE_OF_GRIDS - gx) <= gy);
|
|
}
|
|
else
|
|
quarterIndex = gx > gy;
|
|
|
|
quarterIndex *= 3;
|
|
|
|
return G3D::Plane(
|
|
G3D::Vector3(boundGridCoords[indices[quarterIndex + 0] * 2 + 0], boundGridCoords[indices[quarterIndex + 0] * 2 + 1], _minHeight[indices[quarterIndex + 0]]),
|
|
G3D::Vector3(boundGridCoords[indices[quarterIndex + 1] * 2 + 0], boundGridCoords[indices[quarterIndex + 1] * 2 + 1], _minHeight[indices[quarterIndex + 1]]),
|
|
G3D::Vector3(boundGridCoords[indices[quarterIndex + 2] * 2 + 0], boundGridCoords[indices[quarterIndex + 2] * 2 + 1], _minHeight[indices[quarterIndex + 2]])
|
|
).distance(G3D::Vector3(gx, gy, 0.0f));
|
|
}
|
|
|
|
float GridMap::getLiquidLevel(float x, float y) const
|
|
{
|
|
if (!_liquidMap)
|
|
return _liquidLevel;
|
|
|
|
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
|
|
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
|
|
|
|
int cx_int = ((int)x & (MAP_RESOLUTION-1)) - _liquidOffY;
|
|
int cy_int = ((int)y & (MAP_RESOLUTION-1)) - _liquidOffX;
|
|
|
|
if (cx_int < 0 || cx_int >=_liquidHeight)
|
|
return INVALID_HEIGHT;
|
|
if (cy_int < 0 || cy_int >=_liquidWidth)
|
|
return INVALID_HEIGHT;
|
|
|
|
return _liquidMap[cx_int*_liquidWidth + cy_int];
|
|
}
|
|
|
|
// Why does this return LIQUID data?
|
|
uint8 GridMap::getTerrainType(float x, float y) const
|
|
{
|
|
if (!_liquidFlags)
|
|
return 0;
|
|
|
|
x = 16 * (32 - x/SIZE_OF_GRIDS);
|
|
y = 16 * (32 - y/SIZE_OF_GRIDS);
|
|
int lx = (int)x & 15;
|
|
int ly = (int)y & 15;
|
|
return _liquidFlags[lx*16 + ly];
|
|
}
|
|
|
|
// Get water state on map
|
|
inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data)
|
|
{
|
|
// Check water type (if no water return)
|
|
if (!_liquidType && !_liquidFlags)
|
|
return LIQUID_MAP_NO_WATER;
|
|
|
|
// Get cell
|
|
float cx = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
|
|
float cy = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
|
|
|
|
int x_int = (int)cx & (MAP_RESOLUTION-1);
|
|
int y_int = (int)cy & (MAP_RESOLUTION-1);
|
|
|
|
// Check water type in cell
|
|
int idx=(x_int>>3)*16 + (y_int>>3);
|
|
uint8 type = _liquidFlags ? _liquidFlags[idx] : _liquidType;
|
|
uint32 entry = 0;
|
|
if (_liquidEntry)
|
|
{
|
|
if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(_liquidEntry[idx]))
|
|
{
|
|
entry = liquidEntry->Id;
|
|
type &= MAP_LIQUID_TYPE_DARK_WATER;
|
|
uint32 liqTypeIdx = liquidEntry->Type;
|
|
if (entry < 21)
|
|
{
|
|
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y)))
|
|
{
|
|
uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
|
|
if (!overrideLiquid && area->zone)
|
|
{
|
|
area = sAreaTableStore.LookupEntry(area->zone);
|
|
if (area)
|
|
overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type];
|
|
}
|
|
|
|
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
|
|
{
|
|
entry = overrideLiquid;
|
|
liqTypeIdx = liq->Type;
|
|
}
|
|
}
|
|
}
|
|
|
|
type |= 1 << liqTypeIdx;
|
|
}
|
|
}
|
|
|
|
if (type == 0)
|
|
return LIQUID_MAP_NO_WATER;
|
|
|
|
// Check req liquid type mask
|
|
if (ReqLiquidType && !(ReqLiquidType&type))
|
|
return LIQUID_MAP_NO_WATER;
|
|
|
|
// Check water level:
|
|
// Check water height map
|
|
int lx_int = x_int - _liquidOffY;
|
|
int ly_int = y_int - _liquidOffX;
|
|
if (lx_int < 0 || lx_int >=_liquidHeight)
|
|
return LIQUID_MAP_NO_WATER;
|
|
if (ly_int < 0 || ly_int >=_liquidWidth)
|
|
return LIQUID_MAP_NO_WATER;
|
|
|
|
// Get water level
|
|
float liquid_level = _liquidMap ? _liquidMap[lx_int*_liquidWidth + ly_int] : _liquidLevel;
|
|
// Get ground level (sub 0.2 for fix some errors)
|
|
float ground_level = getHeight(x, y);
|
|
|
|
// Check water level and ground level
|
|
if (liquid_level < ground_level || z < ground_level - 2)
|
|
return LIQUID_MAP_NO_WATER;
|
|
|
|
// All ok in water -> store data
|
|
if (data)
|
|
{
|
|
data->entry = entry;
|
|
data->type_flags = type;
|
|
data->level = liquid_level;
|
|
data->depth_level = ground_level;
|
|
}
|
|
|
|
// For speed check as int values
|
|
float delta = liquid_level - z;
|
|
|
|
if (delta > 2.0f) // Under water
|
|
return LIQUID_MAP_UNDER_WATER;
|
|
if (delta > 0.0f) // In water
|
|
return LIQUID_MAP_IN_WATER;
|
|
if (delta > -0.1f) // Walk on water
|
|
return LIQUID_MAP_WATER_WALK;
|
|
// Above water
|
|
return LIQUID_MAP_ABOVE_WATER;
|
|
}
|
|
|
|
GridMap* Map::GetGrid(float x, float y)
|
|
{
|
|
// half opt method
|
|
int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x
|
|
int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
|
|
|
|
// ensure GridMap is loaded
|
|
EnsureGridCreated(GridCoord(63-gx, 63-gy));
|
|
|
|
return GridMaps[gx][gy];
|
|
}
|
|
|
|
float Map::GetWaterOrGroundLevel(float x, float y, float z, float* ground /*= NULL*/, bool /*swim = false*/, float maxSearchDist /*= 50.0f*/) const
|
|
{
|
|
if (const_cast<Map*>(this)->GetGrid(x, y))
|
|
{
|
|
// we need ground level (including grid height version) for proper return water level in point
|
|
float ground_z = GetHeight(PHASEMASK_NORMAL, x, y, z, true, maxSearchDist);
|
|
if (ground)
|
|
*ground = ground_z;
|
|
|
|
LiquidData liquid_status;
|
|
|
|
ZLiquidStatus res = getLiquidStatus(x, y, ground_z, MAP_ALL_LIQUIDS, &liquid_status);
|
|
return res ? liquid_status.level : ground_z;
|
|
}
|
|
|
|
return VMAP_INVALID_HEIGHT_VALUE;
|
|
}
|
|
|
|
Transport* Map::GetTransportForPos(uint32 phase, float x, float y, float z, WorldObject* worldobject)
|
|
{
|
|
G3D::Vector3 v(x, y, z + 2.0f);
|
|
G3D::Ray r(v, G3D::Vector3(0, 0, -1));
|
|
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
|
if ((*itr)->IsInWorld() && (*itr)->GetExactDistSq(x, y, z) < 75.0f*75.0f && (*itr)->m_model)
|
|
{
|
|
float dist = 30.0f;
|
|
bool hit = (*itr)->m_model->intersectRay(r, dist, false, phase);
|
|
if (hit)
|
|
return *itr;
|
|
}
|
|
|
|
if (worldobject)
|
|
if (GameObject* staticTrans = worldobject->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_TRANSPORT, 75.0f))
|
|
if (staticTrans->m_model)
|
|
{
|
|
float dist = 10.0f;
|
|
bool hit = staticTrans->m_model->intersectRay(r, dist, false, phase);
|
|
if (hit)
|
|
if (GetHeight(phase, x, y, z, true, 30.0f) < (v.z - dist + 1.0f))
|
|
return staticTrans->ToTransport();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) const
|
|
{
|
|
// find raw .map surface under Z coordinates
|
|
float mapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
|
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
|
|
{
|
|
float gridHeight = gmap->getHeight(x, y);
|
|
// look from a bit higher pos to find the floor, ignore under surface case
|
|
if (z + 2.0f > gridHeight)
|
|
mapHeight = gridHeight;
|
|
}
|
|
|
|
float vmapHeight = VMAP_INVALID_HEIGHT_VALUE;
|
|
if (checkVMap)
|
|
{
|
|
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
|
|
//if (vmgr->isHeightCalcEnabled()) // pussywizard: optimization
|
|
vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f, maxSearchDist); // look from a bit higher pos to find the floor
|
|
}
|
|
|
|
// 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 (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)
|
|
}
|
|
|
|
return mapHeight; // explicitly use map data
|
|
}
|
|
|
|
float Map::GetMinHeight(float x, float y) const
|
|
{
|
|
if (GridMap const* grid = const_cast<Map*>(this)->GetGrid(x, y))
|
|
return grid->getMinHeight(x, y);
|
|
|
|
return -500.0f;
|
|
}
|
|
|
|
|
|
inline bool IsOutdoorWMO(uint32 mogpFlags, int32 /*adtId*/, int32 /*rootId*/, int32 /*groupId*/, WMOAreaTableEntry const* wmoEntry, AreaTableEntry const* atEntry)
|
|
{
|
|
bool outdoor = true;
|
|
|
|
if (wmoEntry && atEntry)
|
|
{
|
|
if (atEntry->flags & AREA_FLAG_OUTSIDE)
|
|
return true;
|
|
if (atEntry->flags & AREA_FLAG_INSIDE)
|
|
return false;
|
|
}
|
|
|
|
outdoor = mogpFlags&0x8;
|
|
|
|
if (wmoEntry)
|
|
{
|
|
if (wmoEntry->Flags & 4)
|
|
return true;
|
|
if ((wmoEntry->Flags & 2)!=0)
|
|
outdoor = false;
|
|
}
|
|
return outdoor;
|
|
}
|
|
|
|
bool Map::IsOutdoors(float x, float y, float z) const
|
|
{
|
|
uint32 mogpFlags;
|
|
int32 adtId, rootId, groupId;
|
|
|
|
// no wmo found? -> outside by default
|
|
if (!GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
|
|
return true;
|
|
|
|
AreaTableEntry const* atEntry = 0;
|
|
WMOAreaTableEntry const* wmoEntry= GetWMOAreaTableEntryByTripple(rootId, adtId, groupId);
|
|
if (wmoEntry)
|
|
{
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outStaticDebug("Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId);
|
|
#endif
|
|
atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId);
|
|
}
|
|
return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
|
|
}
|
|
|
|
bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
|
|
{
|
|
float vmap_z = z;
|
|
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
|
|
if (vmgr->getAreaInfo(GetId(), x, y, vmap_z, flags, adtId, rootId, groupId))
|
|
{
|
|
// check if there's terrain between player height and object height
|
|
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
|
|
{
|
|
float _mapheight = gmap->getHeight(x, y);
|
|
// z + 2.0f condition taken from GetHeight(), not sure if it's such a great choice...
|
|
if (z + 2.0f > _mapheight && _mapheight > vmap_z)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const
|
|
{
|
|
uint32 mogpFlags;
|
|
int32 adtId, rootId, groupId;
|
|
WMOAreaTableEntry const* wmoEntry = 0;
|
|
AreaTableEntry const* atEntry = 0;
|
|
bool haveAreaInfo = false;
|
|
|
|
if (GetAreaInfo(x, y, z, mogpFlags, adtId, rootId, groupId))
|
|
{
|
|
haveAreaInfo = true;
|
|
wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId);
|
|
if (wmoEntry)
|
|
atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId);
|
|
}
|
|
|
|
uint16 areaId = 0;
|
|
|
|
if (atEntry)
|
|
areaId = atEntry->ID;
|
|
else
|
|
{
|
|
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
|
|
areaId = gmap->getArea(x, y);
|
|
// this used while not all *.map files generated (instances)
|
|
if (!areaId)
|
|
areaId = i_mapEntry->linked_zone;
|
|
}
|
|
|
|
if (isOutdoors)
|
|
{
|
|
if (haveAreaInfo)
|
|
*isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry);
|
|
else
|
|
*isOutdoors = true;
|
|
}
|
|
return areaId;
|
|
}
|
|
|
|
uint32 Map::GetAreaId(float x, float y, float z) const
|
|
{
|
|
return GetAreaId(x, y, z, nullptr);
|
|
}
|
|
|
|
uint32 Map::GetZoneId(float x, float y, float z) const
|
|
{
|
|
uint32 areaId = GetAreaId(x, y, z);
|
|
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId))
|
|
if (area->zone)
|
|
return area->zone;
|
|
|
|
return areaId;
|
|
}
|
|
|
|
void Map::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const
|
|
{
|
|
areaid = zoneid = GetAreaId(x, y, z);
|
|
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid))
|
|
if (area->zone)
|
|
zoneid = area->zone;
|
|
}
|
|
|
|
uint8 Map::GetTerrainType(float x, float y) const
|
|
{
|
|
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
|
|
return gmap->getTerrainType(x, y);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data) const
|
|
{
|
|
ZLiquidStatus result = LIQUID_MAP_NO_WATER;
|
|
VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();
|
|
float liquid_level = INVALID_HEIGHT;
|
|
float ground_level = INVALID_HEIGHT;
|
|
uint32 liquid_type = 0;
|
|
if (vmgr->GetLiquidLevel(GetId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type))
|
|
{
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDebug(LOG_FILTER_MAPS, "getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type);
|
|
#endif
|
|
// Check water level and ground level
|
|
if (liquid_level > ground_level && z > ground_level - 2)
|
|
{
|
|
// All ok in water -> store data
|
|
if (data)
|
|
{
|
|
// hardcoded in client like this
|
|
if (GetId() == 530 && liquid_type == 2)
|
|
liquid_type = 15;
|
|
|
|
uint32 liquidFlagType = 0;
|
|
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type))
|
|
liquidFlagType = liq->Type;
|
|
|
|
if (liquid_type && liquid_type < 21)
|
|
{
|
|
if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z)))
|
|
{
|
|
uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType];
|
|
if (!overrideLiquid && area->zone)
|
|
{
|
|
area = sAreaTableStore.LookupEntry(area->zone);
|
|
if (area)
|
|
overrideLiquid = area->LiquidTypeOverride[liquidFlagType];
|
|
}
|
|
|
|
if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid))
|
|
{
|
|
liquid_type = overrideLiquid;
|
|
liquidFlagType = liq->Type;
|
|
}
|
|
}
|
|
}
|
|
|
|
data->level = liquid_level;
|
|
data->depth_level = ground_level;
|
|
|
|
data->entry = liquid_type;
|
|
data->type_flags = 1 << liquidFlagType;
|
|
}
|
|
|
|
float delta = liquid_level - z;
|
|
|
|
// Get position delta
|
|
if (delta > 2.0f) // Under water
|
|
return LIQUID_MAP_UNDER_WATER;
|
|
if (delta > 0.0f) // In water
|
|
return LIQUID_MAP_IN_WATER;
|
|
if (delta > -0.1f) // Walk on water
|
|
return LIQUID_MAP_WATER_WALK;
|
|
result = LIQUID_MAP_ABOVE_WATER;
|
|
}
|
|
}
|
|
|
|
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
|
|
{
|
|
LiquidData map_data;
|
|
ZLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, &map_data);
|
|
// Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER:
|
|
if (map_result != LIQUID_MAP_NO_WATER && (map_data.level > ground_level))
|
|
{
|
|
if (data)
|
|
{
|
|
// hardcoded in client like this
|
|
if (GetId() == 530 && map_data.entry == 2)
|
|
map_data.entry = 15;
|
|
|
|
*data = map_data;
|
|
}
|
|
return map_result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
float Map::GetWaterLevel(float x, float y) const
|
|
{
|
|
if (GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
|
|
return gmap->getLiquidLevel(x, y);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const
|
|
{
|
|
return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2)
|
|
&& _dynamicTree.isInLineOfSight(x1, y1, z1, x2, y2, z2, phasemask);
|
|
}
|
|
|
|
bool Map::getObjectHitPos(uint32 phasemask, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist)
|
|
{
|
|
G3D::Vector3 startPos(x1, y1, z1);
|
|
G3D::Vector3 dstPos(x2, y2, z2);
|
|
|
|
G3D::Vector3 resultPos;
|
|
bool result = _dynamicTree.getObjectHitPos(phasemask, startPos, dstPos, resultPos, modifyDist);
|
|
|
|
rx = resultPos.x;
|
|
ry = resultPos.y;
|
|
rz = resultPos.z;
|
|
return result;
|
|
}
|
|
|
|
float Map::GetHeight(uint32 phasemask, float x, float y, float z, bool vmap/*=true*/, float maxSearchDist/*=DEFAULT_HEIGHT_SEARCH*/) const
|
|
{
|
|
float h1, h2;
|
|
h1 = GetHeight(x, y, z, vmap, maxSearchDist);
|
|
h2 = _dynamicTree.getHeight(x, y, z, maxSearchDist, phasemask);
|
|
return std::max<float>(h1, h2);
|
|
}
|
|
|
|
bool Map::IsInWater(float x, float y, float pZ, LiquidData* data) const
|
|
{
|
|
LiquidData liquid_status;
|
|
LiquidData* liquid_ptr = data ? data : &liquid_status;
|
|
return getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER);
|
|
}
|
|
|
|
bool Map::IsUnderWater(float x, float y, float z) const
|
|
{
|
|
return getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN) & LIQUID_MAP_UNDER_WATER;
|
|
}
|
|
|
|
char const* Map::GetMapName() const
|
|
{
|
|
return i_mapEntry ? i_mapEntry->name[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0";
|
|
}
|
|
|
|
void Map::SendInitSelf(Player* player)
|
|
{
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("Creating player data for himself %u", player->GetGUIDLow());
|
|
#endif
|
|
|
|
UpdateData data;
|
|
|
|
// attach to player data current transport data
|
|
if (Transport* transport = player->GetTransport())
|
|
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::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr)
|
|
if (player != (*itr) && player->HaveAtClient(*itr))
|
|
(*itr)->BuildCreateUpdateBlockForPlayer(&data, player);
|
|
|
|
WorldPacket packet;
|
|
data.BuildPacket(&packet);
|
|
player->GetSession()->SendPacket(&packet);
|
|
}
|
|
|
|
void Map::SendInitTransports(Player* player)
|
|
{
|
|
// Hack to send out transports
|
|
UpdateData transData;
|
|
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
|
if (*itr != player->GetTransport())
|
|
(*itr)->BuildCreateUpdateBlockForPlayer(&transData, player);
|
|
|
|
WorldPacket packet;
|
|
transData.BuildPacket(&packet);
|
|
player->GetSession()->SendPacket(&packet);
|
|
}
|
|
|
|
void Map::SendRemoveTransports(Player* player)
|
|
{
|
|
// Hack to send out transports
|
|
UpdateData transData;
|
|
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
|
if (*itr != player->GetTransport())
|
|
(*itr)->BuildOutOfRangeUpdateBlock(&transData);
|
|
|
|
// pussywizard: remove static transports from client
|
|
for (Player::ClientGUIDs::const_iterator it = player->m_clientGUIDs.begin();it != player->m_clientGUIDs.end(); )
|
|
{
|
|
if (IS_TRANSPORT_GUID(*it))
|
|
{
|
|
transData.AddOutOfRangeGUID(*it);
|
|
it = player->m_clientGUIDs.erase(it);
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
|
|
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::DelayedUpdate(const uint32 t_diff)
|
|
{
|
|
for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();)
|
|
{
|
|
MotionTransport* transport = *_transportsUpdateIter;
|
|
++_transportsUpdateIter;
|
|
|
|
if (!transport->IsInWorld())
|
|
continue;
|
|
|
|
transport->DelayedUpdate(t_diff);
|
|
}
|
|
|
|
RemoveAllObjectsInRemoveList();
|
|
}
|
|
|
|
void Map::AddObjectToRemoveList(WorldObject* obj)
|
|
{
|
|
ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId());
|
|
|
|
obj->CleanupsBeforeDelete(false); // remove or simplify at least cross referenced links
|
|
|
|
i_objectsToRemove.insert(obj);
|
|
//sLog->outDebug(LOG_FILTER_MAPS, "Object (GUID: %u TypeId: %u) added to removing list.", obj->GetGUIDLow(), obj->GetTypeId());
|
|
}
|
|
|
|
void Map::AddObjectToSwitchList(WorldObject* obj, bool on)
|
|
{
|
|
ASSERT(obj->GetMapId() == GetId() && obj->GetInstanceId() == GetInstanceId());
|
|
// i_objectsToSwitch is iterated only in Map::RemoveAllObjectsInRemoveList() and it uses
|
|
// the contained objects only if GetTypeId() == TYPEID_UNIT , so we can return in all other cases
|
|
if (obj->GetTypeId() != TYPEID_UNIT && obj->GetTypeId() != TYPEID_GAMEOBJECT)
|
|
return;
|
|
|
|
std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.find(obj);
|
|
if (itr == i_objectsToSwitch.end())
|
|
i_objectsToSwitch.insert(itr, std::make_pair(obj, on));
|
|
else if (itr->second != on)
|
|
i_objectsToSwitch.erase(itr);
|
|
else
|
|
ASSERT(false);
|
|
}
|
|
|
|
void Map::RemoveAllObjectsInRemoveList()
|
|
{
|
|
while (!i_objectsToSwitch.empty())
|
|
{
|
|
std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.begin();
|
|
WorldObject* obj = itr->first;
|
|
bool on = itr->second;
|
|
i_objectsToSwitch.erase(itr);
|
|
|
|
if (!obj->IsPermanentWorldObject())
|
|
{
|
|
switch (obj->GetTypeId())
|
|
{
|
|
case TYPEID_UNIT:
|
|
SwitchGridContainers<Creature>(obj->ToCreature(), on);
|
|
break;
|
|
case TYPEID_GAMEOBJECT:
|
|
SwitchGridContainers<GameObject>(obj->ToGameObject(), on);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//sLog->outDebug(LOG_FILTER_MAPS, "Object remover 1 check.");
|
|
while (!i_objectsToRemove.empty())
|
|
{
|
|
UNORDERED_SET<WorldObject*>::iterator itr = i_objectsToRemove.begin();
|
|
WorldObject* obj = *itr;
|
|
i_objectsToRemove.erase(itr);
|
|
|
|
switch (obj->GetTypeId())
|
|
{
|
|
case TYPEID_CORPSE:
|
|
{
|
|
Corpse* corpse = ObjectAccessor::GetCorpse(*obj, obj->GetGUID());
|
|
if (!corpse)
|
|
sLog->outError("Tried to delete corpse/bones %u that is not in map.", obj->GetGUIDLow());
|
|
else
|
|
RemoveFromMap(corpse, true);
|
|
break;
|
|
}
|
|
case TYPEID_DYNAMICOBJECT:
|
|
RemoveFromMap((DynamicObject*)obj, true);
|
|
break;
|
|
case TYPEID_GAMEOBJECT:
|
|
if (MotionTransport* transport = obj->ToGameObject()->ToMotionTransport())
|
|
RemoveFromMap(transport, true);
|
|
else
|
|
RemoveFromMap(obj->ToGameObject(), true);
|
|
break;
|
|
case TYPEID_UNIT:
|
|
// in case triggered sequence some spell can continue casting after prev CleanupsBeforeDelete call
|
|
// make sure that like sources auras/etc removed before destructor start
|
|
obj->ToCreature()->CleanupsBeforeDelete();
|
|
RemoveFromMap(obj->ToCreature(), true);
|
|
break;
|
|
default:
|
|
sLog->outError("Non-grid object (TypeId: %u) is in grid object remove list, ignored.", obj->GetTypeId());
|
|
break;
|
|
}
|
|
}
|
|
|
|
//sLog->outDebug(LOG_FILTER_MAPS, "Object remover 2 check.");
|
|
}
|
|
|
|
uint32 Map::GetPlayersCountExceptGMs() const
|
|
{
|
|
uint32 count = 0;
|
|
for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
if (!itr->GetSource()->IsGameMaster())
|
|
++count;
|
|
return count;
|
|
}
|
|
|
|
void Map::SendToPlayers(WorldPacket const* data) const
|
|
{
|
|
for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
itr->GetSource()->GetSession()->SendPacket(data);
|
|
}
|
|
|
|
template<class T>
|
|
void Map::AddToActive(T* obj)
|
|
{
|
|
AddToActiveHelper(obj);
|
|
}
|
|
|
|
template <>
|
|
void Map::AddToActive(Creature* c)
|
|
{
|
|
AddToActiveHelper(c);
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToActive(DynamicObject* d)
|
|
{
|
|
AddToActiveHelper(d);
|
|
}
|
|
|
|
template<>
|
|
void Map::AddToActive(GameObject* d)
|
|
{
|
|
AddToActiveHelper(d);
|
|
}
|
|
|
|
template<class T>
|
|
void Map::RemoveFromActive(T* obj)
|
|
{
|
|
RemoveFromActiveHelper(obj);
|
|
}
|
|
|
|
template <>
|
|
void Map::RemoveFromActive(Creature* c)
|
|
{
|
|
RemoveFromActiveHelper(c);
|
|
}
|
|
|
|
template<>
|
|
void Map::RemoveFromActive(DynamicObject* obj)
|
|
{
|
|
RemoveFromActiveHelper(obj);
|
|
}
|
|
|
|
template<>
|
|
void Map::RemoveFromActive(GameObject* obj)
|
|
{
|
|
RemoveFromActiveHelper(obj);
|
|
}
|
|
|
|
template bool Map::AddToMap(Corpse*, bool);
|
|
template bool Map::AddToMap(Creature*, bool);
|
|
template bool Map::AddToMap(GameObject*, bool);
|
|
template bool Map::AddToMap(DynamicObject*, bool);
|
|
|
|
template void Map::RemoveFromMap(Corpse*, bool);
|
|
template void Map::RemoveFromMap(Creature*, bool);
|
|
template void Map::RemoveFromMap(GameObject*, bool);
|
|
template void Map::RemoveFromMap(DynamicObject*, bool);
|
|
|
|
/* ******* Dungeon Instance Maps ******* */
|
|
|
|
InstanceMap::InstanceMap(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent)
|
|
: Map(id, InstanceId, SpawnMode, _parent),
|
|
m_resetAfterUnload(false), m_unloadWhenEmpty(false),
|
|
instance_script(NULL), i_script_id(0)
|
|
{
|
|
//lets initialize visibility distance for dungeons
|
|
InstanceMap::InitVisibilityDistance();
|
|
|
|
// 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->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
|
|
|
|
// pussywizard:
|
|
if (IsRaid())
|
|
if (time_t resetTime = sInstanceSaveMgr->GetResetTimeFor(id, Difficulty(SpawnMode)))
|
|
if (time_t extendedResetTime = sInstanceSaveMgr->GetExtendedResetTimeFor(id, Difficulty(SpawnMode)))
|
|
_instanceResetPeriod = extendedResetTime - resetTime;
|
|
}
|
|
|
|
InstanceMap::~InstanceMap()
|
|
{
|
|
delete instance_script;
|
|
instance_script = NULL;
|
|
sInstanceSaveMgr->DeleteInstanceSaveIfNeeded(GetInstanceId(), true);
|
|
}
|
|
|
|
void InstanceMap::InitVisibilityDistance()
|
|
{
|
|
//init visibility distance for instances
|
|
m_VisibleDistance = World::GetMaxVisibleDistanceInInstances();
|
|
|
|
// pussywizard: this CAN NOT exceed MAX_VISIBILITY_DISTANCE
|
|
switch (GetId())
|
|
{
|
|
case 429: // Dire Maul
|
|
m_VisibleDistance = 175.0f;
|
|
break;
|
|
case 649: // Trial of the Crusader
|
|
case 650: // Trial of the Champion
|
|
case 595: // Culling of Startholme
|
|
case 658: // Pit of Saron
|
|
m_VisibleDistance = 150.0f;
|
|
break;
|
|
case 550: // The Eye
|
|
case 578: // The Nexus: The Oculus
|
|
m_VisibleDistance = 175.0f;
|
|
break;
|
|
case 615: // Obsidian Sanctum
|
|
case 616: // Eye of Eternity
|
|
case 603: // Ulduar
|
|
case 668: // Halls of Reflection
|
|
case 631: // Icecrown Citadel
|
|
case 724: // Ruby Sanctum
|
|
m_VisibleDistance = 200.0f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Do map specific checks to see if the player can enter
|
|
*/
|
|
bool InstanceMap::CanEnter(Player* player, bool loginCheck)
|
|
{
|
|
if (!loginCheck && player->GetMapRef().getTarget() == this)
|
|
{
|
|
sLog->outError("InstanceMap::CanEnter - player %s(%u) already in map %d, %d, %d!", player->GetName().c_str(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
|
|
ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
// allow GM's to enter
|
|
if (player->IsGameMaster())
|
|
return Map::CanEnter(player, loginCheck);
|
|
|
|
// cannot enter if the instance is full (player cap), GMs don't count
|
|
uint32 maxPlayers = GetMaxPlayers();
|
|
if (GetPlayersCountExceptGMs() >= (loginCheck ? maxPlayers+1 : maxPlayers))
|
|
{
|
|
#if defined(ENABLE_EXTRAS) && defined(ENABLE_EXTRA_LOGS)
|
|
sLog->outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), maxPlayers, player->GetName().c_str());
|
|
#endif
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
|
|
return false;
|
|
}
|
|
|
|
// cannot enter while an encounter is in progress on raids
|
|
bool checkProgress = (IsRaid() || GetId() == 668 /*HoR*/);
|
|
if (checkProgress && GetInstanceScript() && GetInstanceScript()->IsEncounterInProgress())
|
|
{
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT);
|
|
return false;
|
|
}
|
|
|
|
// xinef: dont allow LFG Group to enter other instance that is selected
|
|
if (Group* group = player->GetGroup())
|
|
if (group->isLFGGroup())
|
|
if (!sLFGMgr->inLfgDungeonMap(group->GetGUID(), GetId(), GetDifficulty()))
|
|
{
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAP_NOT_ALLOWED);
|
|
return false;
|
|
}
|
|
|
|
// cannot enter if instance is in use by another party/soloer that have a permanent save in the same instance id
|
|
PlayerList const &playerList = GetPlayers();
|
|
if (!playerList.isEmpty())
|
|
for (PlayerList::const_iterator i = playerList.begin(); i != playerList.end(); ++i)
|
|
if (Player* iPlayer = i->GetSource())
|
|
{
|
|
if (iPlayer == player) // login case, player already added to map
|
|
continue;
|
|
if (iPlayer->IsGameMaster()) // bypass GMs
|
|
continue;
|
|
if (!player->GetGroup()) // player has not group and there is someone inside, deny entry
|
|
{
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
|
|
return false;
|
|
}
|
|
// player inside instance has no group or his groups is different to entering player's one, deny entry
|
|
if (!iPlayer->GetGroup() || iPlayer->GetGroup() != player->GetGroup())
|
|
{
|
|
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return Map::CanEnter(player, loginCheck);
|
|
}
|
|
|
|
/*
|
|
Do map specific checks and add the player to the map if successful.
|
|
*/
|
|
bool InstanceMap::AddPlayerToMap(Player* player)
|
|
{
|
|
if (m_resetAfterUnload) // this instance has been reset, it's not meant to be used anymore
|
|
return false;
|
|
|
|
if (IsDungeon())
|
|
{
|
|
Group* group = player->GetGroup();
|
|
|
|
// get an instance save for the map
|
|
InstanceSave* mapSave = sInstanceSaveMgr->GetInstanceSave(GetInstanceId());
|
|
if (!mapSave)
|
|
{
|
|
sLog->outError("InstanceMap::Add: InstanceSave does not exist for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId());
|
|
return false;
|
|
}
|
|
|
|
// check for existing instance binds
|
|
InstancePlayerBind* playerBind = sInstanceSaveMgr->PlayerGetBoundInstance(player->GetGUIDLow(), GetId(), Difficulty(GetSpawnMode()));
|
|
if (playerBind && playerBind->perm)
|
|
{
|
|
if (playerBind->save != mapSave)
|
|
{
|
|
sLog->outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d, %d, %d, %d but he is being put into instance %d, %d, %d, %d", player->GetName().c_str(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->CanReset());
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
playerBind = sInstanceSaveMgr->PlayerBindToInstance(player->GetGUIDLow(), mapSave, false, player);
|
|
// pussywizard: bind lider also if not yet bound
|
|
if (Group* g = player->GetGroup())
|
|
if (g->GetLeaderGUID() != player->GetGUID())
|
|
if (!sInstanceSaveMgr->PlayerGetBoundInstance(GUID_LOPART(g->GetLeaderGUID()), mapSave->GetMapId(), mapSave->GetDifficulty()))
|
|
{
|
|
sInstanceSaveMgr->PlayerCreateBoundInstancesMaps(GUID_LOPART(g->GetLeaderGUID()));
|
|
sInstanceSaveMgr->PlayerBindToInstance(GUID_LOPART(g->GetLeaderGUID()), mapSave, false, ObjectAccessor::FindPlayerInOrOutOfWorld(g->GetLeaderGUID()));
|
|
}
|
|
}
|
|
|
|
// increase current instances (hourly limit)
|
|
// xinef: specific instances are still limited
|
|
if (!group || !group->isLFGGroup() || !group->IsLfgRandomInstance())
|
|
player->AddInstanceEnterTime(GetInstanceId(), time(NULL));
|
|
|
|
if (!playerBind->perm && !mapSave->CanReset() && (!group || !group->isLFGGroup() || !group->IsLfgRandomInstance()))
|
|
{
|
|
WorldPacket data(SMSG_INSTANCE_LOCK_WARNING_QUERY, 9);
|
|
data << uint32(60000);
|
|
data << uint32(instance_script ? instance_script->GetCompletedEncounterMask() : 0);
|
|
data << uint8(0);
|
|
player->GetSession()->SendPacket(&data);
|
|
player->SetPendingBind(mapSave->GetInstanceId(), 60000);
|
|
}
|
|
}
|
|
|
|
// 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::AddPlayerToMap(player);
|
|
|
|
if (instance_script)
|
|
instance_script->OnPlayerEnter(player);
|
|
|
|
return true;
|
|
}
|
|
|
|
void InstanceMap::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/)
|
|
{
|
|
Map::Update(t_diff, s_diff);
|
|
|
|
if (t_diff)
|
|
if (instance_script)
|
|
instance_script->Update(t_diff);
|
|
}
|
|
|
|
void InstanceMap::RemovePlayerFromMap(Player* player, bool remove)
|
|
{
|
|
// pussywizard: moved m_unloadTimer to InstanceMap::AfterPlayerUnlinkFromMap(), in this function if 2 players run out at the same time the instance won't close
|
|
//if (!m_unloadTimer && m_mapRefManager.getSize() == 1)
|
|
// m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
|
|
Map::RemovePlayerFromMap(player, remove);
|
|
}
|
|
|
|
void InstanceMap::AfterPlayerUnlinkFromMap()
|
|
{
|
|
if (!m_unloadTimer && !HavePlayers())
|
|
m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld->getIntConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY);
|
|
Map::AfterPlayerUnlinkFromMap();
|
|
}
|
|
|
|
void InstanceMap::CreateInstanceScript(bool load, std::string data, uint32 completedEncounterMask)
|
|
{
|
|
if (instance_script != NULL)
|
|
return;
|
|
|
|
InstanceTemplate const* mInstance = sObjectMgr->GetInstanceTemplate(GetId());
|
|
if (mInstance)
|
|
{
|
|
i_script_id = mInstance->ScriptId;
|
|
instance_script = sScriptMgr->CreateInstanceScript(this);
|
|
}
|
|
|
|
if (!instance_script)
|
|
return;
|
|
|
|
instance_script->Initialize();
|
|
|
|
if (load)
|
|
{
|
|
instance_script->SetCompletedEncountersMask(completedEncounterMask, false);
|
|
if (data != "")
|
|
instance_script->Load(data.c_str());
|
|
}
|
|
}
|
|
|
|
/*
|
|
Returns true if there are no players in the instance
|
|
*/
|
|
bool InstanceMap::Reset(uint8 method, std::list<uint32>* globalResetSkipList)
|
|
{
|
|
if (method == INSTANCE_RESET_GLOBAL)
|
|
{
|
|
// pussywizard: teleport out immediately
|
|
for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
{
|
|
// teleport players that are no longer bound (can be still bound if extended id)
|
|
if (!globalResetSkipList || std::find(globalResetSkipList->begin(), globalResetSkipList->end(), itr->GetSource()->GetGUIDLow()) == globalResetSkipList->end())
|
|
itr->GetSource()->RepopAtGraveyard();
|
|
}
|
|
|
|
// reset map only if noone is bound
|
|
if (!globalResetSkipList || globalResetSkipList->empty())
|
|
{
|
|
// pussywizard: setting both m_unloadWhenEmpty and m_unloadTimer intended, in case RepopAtGraveyard failed
|
|
if (HavePlayers())
|
|
m_unloadWhenEmpty = true;
|
|
m_unloadTimer = MIN_UNLOAD_DELAY;
|
|
m_resetAfterUnload = true;
|
|
}
|
|
|
|
return m_mapRefManager.isEmpty();
|
|
}
|
|
|
|
if (HavePlayers())
|
|
{
|
|
if (method == INSTANCE_RESET_ALL || method == INSTANCE_RESET_CHANGE_DIFFICULTY)
|
|
{
|
|
for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
itr->GetSource()->SendResetFailedNotify(GetId());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_unloadTimer = MIN_UNLOAD_DELAY;
|
|
m_resetAfterUnload = true;
|
|
}
|
|
|
|
return m_mapRefManager.isEmpty();
|
|
}
|
|
|
|
void InstanceMap::PermBindAllPlayers()
|
|
{
|
|
if (!IsDungeon())
|
|
return;
|
|
|
|
InstanceSave* save = sInstanceSaveMgr->GetInstanceSave(GetInstanceId());
|
|
if (!save)
|
|
{
|
|
sLog->outError("Cannot bind players because no instance save is available for instance map (Name: %s, Entry: %u, InstanceId: %u)!", GetMapName(), GetId(), GetInstanceId());
|
|
return;
|
|
}
|
|
|
|
Player* player;
|
|
Group* group;
|
|
// group members outside the instance group don't get bound
|
|
for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
{
|
|
player = itr->GetSource();
|
|
group = player->GetGroup();
|
|
|
|
// players inside an instance cannot be bound to other instances
|
|
// some players may already be permanently bound, in this case nothing happens
|
|
InstancePlayerBind* bind = sInstanceSaveMgr->PlayerGetBoundInstance(player->GetGUIDLow(), save->GetMapId(), save->GetDifficulty());
|
|
|
|
if (!bind || !bind->perm)
|
|
{
|
|
WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4);
|
|
data << uint32(0);
|
|
player->GetSession()->SendPacket(&data);
|
|
sInstanceSaveMgr->PlayerBindToInstance(player->GetGUIDLow(), save, true, player);
|
|
}
|
|
|
|
// Xinef: Difficulty change prevention
|
|
if (group)
|
|
group->SetDifficultyChangePrevention(DIFFICULTY_PREVENTION_CHANGE_BOSS_KILLED);
|
|
}
|
|
}
|
|
|
|
void InstanceMap::UnloadAll()
|
|
{
|
|
ASSERT(!HavePlayers());
|
|
|
|
if (m_resetAfterUnload == true)
|
|
DeleteRespawnTimes();
|
|
|
|
Map::UnloadAll();
|
|
}
|
|
|
|
void InstanceMap::SendResetWarnings(uint32 timeLeft) const
|
|
{
|
|
for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
itr->GetSource()->SendInstanceResetWarning(GetId(), itr->GetSource()->GetDifficulty(IsRaid()), timeLeft, false);
|
|
}
|
|
|
|
MapDifficulty const* Map::GetMapDifficulty() const
|
|
{
|
|
return GetMapDifficultyData(GetId(), GetDifficulty());
|
|
}
|
|
|
|
uint32 InstanceMap::GetMaxPlayers() const
|
|
{
|
|
MapDifficulty const* mapDiff = GetMapDifficulty();
|
|
if (mapDiff && mapDiff->maxPlayers)
|
|
return mapDiff->maxPlayers;
|
|
|
|
return GetEntry()->maxPlayers;
|
|
}
|
|
|
|
uint32 InstanceMap::GetMaxResetDelay() const
|
|
{
|
|
MapDifficulty const* mapDiff = GetMapDifficulty();
|
|
return mapDiff ? mapDiff->resetTime : 0;
|
|
}
|
|
|
|
/* ******* Battleground Instance Maps ******* */
|
|
|
|
BattlegroundMap::BattlegroundMap(uint32 id, uint32 InstanceId, Map* _parent, uint8 spawnMode)
|
|
: Map(id, InstanceId, spawnMode, _parent), m_bg(NULL)
|
|
{
|
|
//lets initialize visibility distance for BG/Arenas
|
|
BattlegroundMap::InitVisibilityDistance();
|
|
}
|
|
|
|
BattlegroundMap::~BattlegroundMap()
|
|
{
|
|
if (m_bg)
|
|
{
|
|
//unlink to prevent crash, always unlink all pointer reference before destruction
|
|
m_bg->SetBgMap(NULL);
|
|
m_bg = NULL;
|
|
}
|
|
}
|
|
|
|
void BattlegroundMap::InitVisibilityDistance()
|
|
{
|
|
//init visibility distance for BG/Arenas
|
|
m_VisibleDistance = World::GetMaxVisibleDistanceInBGArenas();
|
|
|
|
if (IsBattleArena()) // pussywizard: start with 30yd visibility range on arenas to ensure players can't get informations about the opponents in any way
|
|
m_VisibleDistance = 30.0f;
|
|
}
|
|
|
|
bool BattlegroundMap::CanEnter(Player* player, bool loginCheck)
|
|
{
|
|
if (!loginCheck && player->GetMapRef().getTarget() == this)
|
|
{
|
|
sLog->outError("BGMap::CanEnter - player %u is already in map!", player->GetGUIDLow());
|
|
ASSERT(false);
|
|
return false;
|
|
}
|
|
|
|
if (player->GetBattlegroundId() != GetInstanceId())
|
|
return false;
|
|
|
|
// pussywizard: no need to check player limit here, invitations are limited by Battleground::GetFreeSlotsForTeam
|
|
|
|
return Map::CanEnter(player, loginCheck);
|
|
}
|
|
|
|
bool BattlegroundMap::AddPlayerToMap(Player* player)
|
|
{
|
|
player->m_InstanceValid = true;
|
|
if (IsBattleArena())
|
|
player->CastSpell(player, 100102, true);
|
|
return Map::AddPlayerToMap(player);
|
|
}
|
|
|
|
void BattlegroundMap::RemovePlayerFromMap(Player* player, bool remove)
|
|
{
|
|
if (Battleground* bg = GetBG())
|
|
{
|
|
bg->RemovePlayerAtLeave(player);
|
|
if (IsBattleArena())
|
|
bg->RemoveSpectator(player);
|
|
}
|
|
if (IsBattleArena())
|
|
player->RemoveAura(100102);
|
|
Map::RemovePlayerFromMap(player, remove);
|
|
}
|
|
|
|
void BattlegroundMap::SetUnload()
|
|
{
|
|
m_unloadTimer = MIN_UNLOAD_DELAY;
|
|
}
|
|
|
|
void BattlegroundMap::RemoveAllPlayers()
|
|
{
|
|
if (HavePlayers())
|
|
for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
|
|
if (Player* player = itr->GetSource())
|
|
if (!player->IsBeingTeleportedFar())
|
|
player->TeleportTo(player->GetEntryPoint());
|
|
}
|
|
|
|
Player* Map::GetPlayer(uint64 guid)
|
|
{
|
|
return ObjectAccessor::GetObjectInMap(guid, this, (Player*)NULL);
|
|
}
|
|
|
|
Creature* Map::GetCreature(uint64 guid)
|
|
{
|
|
return ObjectAccessor::GetObjectInMap(guid, this, (Creature*)NULL);
|
|
}
|
|
|
|
GameObject* Map::GetGameObject(uint64 guid)
|
|
{
|
|
return ObjectAccessor::GetObjectInMap(guid, this, (GameObject*)NULL);
|
|
}
|
|
|
|
Transport* Map::GetTransport(uint64 guid)
|
|
{
|
|
if (GUID_HIPART(guid) != HIGHGUID_MO_TRANSPORT && GUID_HIPART(guid) != HIGHGUID_TRANSPORT)
|
|
return NULL;
|
|
|
|
GameObject* go = GetGameObject(guid);
|
|
return go ? go->ToTransport() : NULL;
|
|
}
|
|
|
|
DynamicObject* Map::GetDynamicObject(uint64 guid)
|
|
{
|
|
return ObjectAccessor::GetObjectInMap(guid, this, (DynamicObject*)NULL);
|
|
}
|
|
|
|
Pet* Map::GetPet(uint64 guid)
|
|
{
|
|
return ObjectAccessor::GetObjectInMap(guid, this, (Pet*)NULL);
|
|
}
|
|
|
|
Corpse* Map::GetCorpse(uint64 guid)
|
|
{
|
|
return ObjectAccessor::GetObjectInMap(guid, this, (Corpse*)NULL);
|
|
}
|
|
|
|
void Map::UpdateIteratorBack(Player* player)
|
|
{
|
|
if (m_mapRefIter == player->GetMapRef())
|
|
m_mapRefIter = m_mapRefIter->nocheck_prev();
|
|
}
|
|
|
|
void Map::SaveCreatureRespawnTime(uint32 dbGuid, time_t& respawnTime)
|
|
{
|
|
if (!respawnTime)
|
|
{
|
|
// Delete only
|
|
RemoveCreatureRespawnTime(dbGuid);
|
|
return;
|
|
}
|
|
|
|
time_t now = time(NULL);
|
|
if (GetInstanceResetPeriod() > 0 && respawnTime-now+5 >= GetInstanceResetPeriod())
|
|
respawnTime = now+YEAR;
|
|
|
|
_creatureRespawnTimes[dbGuid] = respawnTime;
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN);
|
|
stmt->setUInt32(0, dbGuid);
|
|
stmt->setUInt32(1, uint32(respawnTime));
|
|
stmt->setUInt16(2, GetId());
|
|
stmt->setUInt32(3, GetInstanceId());
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
void Map::RemoveCreatureRespawnTime(uint32 dbGuid)
|
|
{
|
|
_creatureRespawnTimes.erase(dbGuid);
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN);
|
|
stmt->setUInt32(0, dbGuid);
|
|
stmt->setUInt16(1, GetId());
|
|
stmt->setUInt32(2, GetInstanceId());
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
void Map::SaveGORespawnTime(uint32 dbGuid, time_t& respawnTime)
|
|
{
|
|
if (!respawnTime)
|
|
{
|
|
// Delete only
|
|
RemoveGORespawnTime(dbGuid);
|
|
return;
|
|
}
|
|
|
|
time_t now = time(NULL);
|
|
if (GetInstanceResetPeriod() > 0 && respawnTime-now+5 >= GetInstanceResetPeriod())
|
|
respawnTime = now+YEAR;
|
|
|
|
_goRespawnTimes[dbGuid] = respawnTime;
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN);
|
|
stmt->setUInt32(0, dbGuid);
|
|
stmt->setUInt32(1, uint32(respawnTime));
|
|
stmt->setUInt16(2, GetId());
|
|
stmt->setUInt32(3, GetInstanceId());
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
void Map::RemoveGORespawnTime(uint32 dbGuid)
|
|
{
|
|
_goRespawnTimes.erase(dbGuid);
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN);
|
|
stmt->setUInt32(0, dbGuid);
|
|
stmt->setUInt16(1, GetId());
|
|
stmt->setUInt32(2, GetInstanceId());
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
void Map::LoadRespawnTimes()
|
|
{
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CREATURE_RESPAWNS);
|
|
stmt->setUInt16(0, GetId());
|
|
stmt->setUInt32(1, GetInstanceId());
|
|
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
|
{
|
|
do
|
|
{
|
|
Field* fields = result->Fetch();
|
|
uint32 loguid = fields[0].GetUInt32();
|
|
uint32 respawnTime = fields[1].GetUInt32();
|
|
|
|
_creatureRespawnTimes[loguid] = time_t(respawnTime);
|
|
} while (result->NextRow());
|
|
}
|
|
|
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GO_RESPAWNS);
|
|
stmt->setUInt16(0, GetId());
|
|
stmt->setUInt32(1, GetInstanceId());
|
|
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
|
|
{
|
|
do
|
|
{
|
|
Field* fields = result->Fetch();
|
|
uint32 loguid = fields[0].GetUInt32();
|
|
uint32 respawnTime = fields[1].GetUInt32();
|
|
|
|
_goRespawnTimes[loguid] = time_t(respawnTime);
|
|
} while (result->NextRow());
|
|
}
|
|
}
|
|
|
|
void Map::DeleteRespawnTimes()
|
|
{
|
|
_creatureRespawnTimes.clear();
|
|
_goRespawnTimes.clear();
|
|
|
|
DeleteRespawnTimesInDB(GetId(), GetInstanceId());
|
|
}
|
|
|
|
void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId)
|
|
{
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE);
|
|
stmt->setUInt16(0, mapId);
|
|
stmt->setUInt32(1, instanceId);
|
|
CharacterDatabase.Execute(stmt);
|
|
|
|
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE);
|
|
stmt->setUInt16(0, mapId);
|
|
stmt->setUInt32(1, instanceId);
|
|
CharacterDatabase.Execute(stmt);
|
|
}
|
|
|
|
void Map::UpdateEncounterState(EncounterCreditType type, uint32 creditEntry, Unit* source)
|
|
{
|
|
Difficulty difficulty_fixed = (IsSharedDifficultyMap(GetId()) ? Difficulty(GetDifficulty()%2) : GetDifficulty());
|
|
DungeonEncounterList const* encounters = sObjectMgr->GetDungeonEncounterList(GetId(), difficulty_fixed);
|
|
if (!encounters)
|
|
return;
|
|
|
|
uint32 dungeonId = 0;
|
|
|
|
for (DungeonEncounterList::const_iterator itr = encounters->begin(); itr != encounters->end(); ++itr)
|
|
{
|
|
DungeonEncounter const* encounter = *itr;
|
|
if (encounter->creditType == type && encounter->creditEntry == creditEntry)
|
|
{
|
|
if (source)
|
|
if (InstanceScript* instanceScript = source->GetInstanceScript())
|
|
instanceScript->SetCompletedEncountersMask((1 << encounter->dbcEntry->encounterIndex)|instanceScript->GetCompletedEncounterMask(), true);
|
|
|
|
if (encounter->lastEncounterDungeon)
|
|
{
|
|
dungeonId = encounter->lastEncounterDungeon;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// pussywizard:
|
|
LogEncounterFinished(type, creditEntry);
|
|
|
|
if (dungeonId)
|
|
{
|
|
Map::PlayerList const& players = GetPlayers();
|
|
for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
|
|
{
|
|
if (Player* player = i->GetSource())
|
|
if (Group* grp = player->GetGroup())
|
|
if (grp->isLFGGroup())
|
|
{
|
|
sLFGMgr->FinishDungeon(grp->GetGUID(), dungeonId, this);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Map::LogEncounterFinished(EncounterCreditType type, uint32 creditEntry)
|
|
{
|
|
if (!IsRaid() || !GetEntry() || GetEntry()->Expansion() < 2) // only for wotlk raids, because logs take up tons of mysql memory
|
|
return;
|
|
InstanceMap* map = ToInstanceMap();
|
|
if (!map)
|
|
return;
|
|
std::string playersInfo;
|
|
char buffer[16384], buffer2[255];
|
|
Map::PlayerList const& pl = map->GetPlayers();
|
|
for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
|
|
if (Player* p = itr->GetSource())
|
|
{
|
|
std::string auraStr;
|
|
const Unit::AuraApplicationMap& a = p->GetAppliedAuras();
|
|
for (Unit::AuraApplicationMap::const_iterator itr = a.begin(); itr != a.end(); ++itr)
|
|
{
|
|
snprintf(buffer2, 255, "%u(%u) ", itr->first, itr->second->GetEffectMask());
|
|
auraStr += buffer2;
|
|
}
|
|
|
|
snprintf(buffer, 16384, "%s (guid: %u, acc: %u, ip: %s, guild: %u), xyz: (%.1f, %.1f, %.1f), auras: %s\n", p->GetName().c_str(), p->GetGUIDLow(), p->GetSession()->GetAccountId(), p->GetSession()->GetRemoteAddress().c_str(), p->GetGuildId(), p->GetPositionX(), p->GetPositionY(), p->GetPositionZ(), auraStr.c_str());
|
|
playersInfo += buffer;
|
|
}
|
|
CleanStringForMysqlQuery(playersInfo);
|
|
CharacterDatabase.PExecute("INSERT INTO log_encounter VALUES(NOW(), %u, %u, %u, %u, '%s')", GetId(), (uint32)GetDifficulty(), type, creditEntry, playersInfo.c_str());
|
|
}
|
|
|
|
bool Map::AllTransportsEmpty() const
|
|
{
|
|
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
|
if (!(*itr)->GetPassengers().empty())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Map::AllTransportsRemovePassengers()
|
|
{
|
|
for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr)
|
|
while (!(*itr)->GetPassengers().empty())
|
|
(*itr)->RemovePassenger(*((*itr)->GetPassengers().begin()), true);
|
|
}
|
|
|
|
time_t Map::GetLinkedRespawnTime(uint64 guid) const
|
|
{
|
|
uint64 linkedGuid = sObjectMgr->GetLinkedRespawnGuid(guid);
|
|
switch (GUID_HIPART(linkedGuid))
|
|
{
|
|
case HIGHGUID_UNIT:
|
|
return GetCreatureRespawnTime(GUID_LOPART(linkedGuid));
|
|
case HIGHGUID_GAMEOBJECT:
|
|
return GetGORespawnTime(GUID_LOPART(linkedGuid));
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return time_t(0);
|
|
}
|
|
|
|
void Map::SendZoneDynamicInfo(Player* player)
|
|
{
|
|
uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
|
|
ZoneDynamicInfoMap::const_iterator itr = _zoneDynamicInfo.find(zoneId);
|
|
if (itr == _zoneDynamicInfo.end())
|
|
return;
|
|
|
|
if (uint32 music = itr->second.MusicId)
|
|
{
|
|
WorldPacket data(SMSG_PLAY_MUSIC, 4);
|
|
data << uint32(music);
|
|
player->SendDirectMessage(&data);
|
|
}
|
|
|
|
if (uint32 weather = itr->second.WeatherId)
|
|
{
|
|
WorldPacket data(SMSG_WEATHER, 4 + 4 + 1);
|
|
data << uint32(weather);
|
|
data << float(itr->second.WeatherGrade);
|
|
data << uint8(0);
|
|
player->SendDirectMessage(&data);
|
|
}
|
|
|
|
if (uint32 overrideLight = itr->second.OverrideLightId)
|
|
{
|
|
WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 1);
|
|
data << uint32(_defaultLight);
|
|
data << uint32(overrideLight);
|
|
data << uint32(itr->second.LightFadeInTime);
|
|
player->SendDirectMessage(&data);
|
|
}
|
|
}
|
|
|
|
void Map::SetZoneMusic(uint32 zoneId, uint32 musicId)
|
|
{
|
|
if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end())
|
|
_zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo()));
|
|
|
|
_zoneDynamicInfo[zoneId].MusicId = musicId;
|
|
|
|
Map::PlayerList const& players = GetPlayers();
|
|
if (!players.isEmpty())
|
|
{
|
|
WorldPacket data(SMSG_PLAY_MUSIC, 4);
|
|
data << uint32(musicId);
|
|
|
|
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
|
if (Player* player = itr->GetSource())
|
|
if (player->GetZoneId() == zoneId)
|
|
player->SendDirectMessage(&data);
|
|
}
|
|
}
|
|
|
|
void Map::SetZoneWeather(uint32 zoneId, uint32 weatherId, float weatherGrade)
|
|
{
|
|
if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end())
|
|
_zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo()));
|
|
|
|
ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
|
|
info.WeatherId = weatherId;
|
|
info.WeatherGrade = weatherGrade;
|
|
Map::PlayerList const& players = GetPlayers();
|
|
|
|
if (!players.isEmpty())
|
|
{
|
|
WorldPacket data(SMSG_WEATHER, 4 + 4 + 1);
|
|
data << uint32(weatherId);
|
|
data << float(weatherGrade);
|
|
data << uint8(0);
|
|
|
|
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
|
if (Player* player = itr->GetSource())
|
|
if (player->GetZoneId() == zoneId)
|
|
player->SendDirectMessage(&data);
|
|
}
|
|
}
|
|
|
|
void Map::SetZoneOverrideLight(uint32 zoneId, uint32 lightId, uint32 fadeInTime)
|
|
{
|
|
if (_zoneDynamicInfo.find(zoneId) == _zoneDynamicInfo.end())
|
|
_zoneDynamicInfo.insert(ZoneDynamicInfoMap::value_type(zoneId, ZoneDynamicInfo()));
|
|
|
|
ZoneDynamicInfo& info = _zoneDynamicInfo[zoneId];
|
|
info.OverrideLightId = lightId;
|
|
info.LightFadeInTime = fadeInTime;
|
|
Map::PlayerList const& players = GetPlayers();
|
|
|
|
if (!players.isEmpty())
|
|
{
|
|
WorldPacket data(SMSG_OVERRIDE_LIGHT, 4 + 4 + 4);
|
|
data << uint32(_defaultLight);
|
|
data << uint32(lightId);
|
|
data << uint32(fadeInTime);
|
|
|
|
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
|
|
if (Player* player = itr->GetSource())
|
|
if (player->GetZoneId() == zoneId)
|
|
player->SendDirectMessage(&data);
|
|
}
|
|
}
|