/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "Map.h"
#include "Battleground.h"
#include "CellImpl.h"
#include "Conversation.h"
#include "DatabaseEnv.h"
#include "DisableMgr.h"
#include "DynamicTree.h"
#include "GameObjectModel.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "GridStates.h"
#include "Group.h"
#include "InstancePackets.h"
#include "InstanceScenario.h"
#include "InstanceScript.h"
#include "Log.h"
#include "MapInstanced.h"
#include "MapManager.h"
#include "MiscPackets.h"
#include "MMapFactory.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ObjectGridLoader.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "PhasingHandler.h"
#include "ScriptMgr.h"
#include "Transport.h"
#include "Vehicle.h"
#include "VMapFactory.h"
#include "Weather.h"
#include "WeatherMgr.h"
#include "World.h"
#include "WorldSession.h"
u_map_magic MapMagic = { {'M','A','P','S'} };
u_map_magic MapVersionMagic = { {'v','1','.','9'} };
u_map_magic MapAreaMagic = { {'A','R','E','A'} };
u_map_magic MapHeightMagic = { {'M','H','G','T'} };
u_map_magic MapLiquidMagic = { {'M','L','I','Q'} };
#define DEFAULT_GRID_EXPIRY 300
#define MAX_GRID_LOAD_TIME 50
#define MAX_CREATURE_ATTACK_RADIUS (45.0f * sWorld->getRate(RATE_CREATURE_AGGRO))
GridState* si_GridStates[MAX_GRID_STATE];
ZoneDynamicInfo::ZoneDynamicInfo() : MusicId(0), DefaultWeather(nullptr), WeatherId(WEATHER_STATE_FINE),
WeatherGrade(0.0f), OverrideLightId(0), LightFadeInTime(0) { }
Map::~Map()
{
// UnloadAll must be called before deleting the map
sScriptMgr->OnDestroyMap(this);
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())
sMapMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
if (m_parentMap == this)
delete m_childTerrainMaps;
MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
bool Map::ExistMap(uint32 mapid, int gx, int gy)
{
std::string fileName = Trinity::StringFormat("%smaps/%04u_%02u_%02u.map", sWorld->GetDataPath().c_str(), mapid, gx, gy);
bool ret = false;
FILE* file = fopen(fileName.c_str(), "rb");
if (!file)
{
TC_LOG_ERROR("maps", "Map file '%s' does not exist!", fileName.c_str());
TC_LOG_ERROR("maps", "Please place MAP-files (*.map) in the appropriate directory (%s), or correct the DataDir setting in your worldserver.conf file.", (sWorld->GetDataPath()+"maps/").c_str());
}
else
{
map_fileheader header;
if (fread(&header, sizeof(header), 1, file) == 1)
{
if (header.mapMagic.asUInt != MapMagic.asUInt || header.versionMagic.asUInt != MapVersionMagic.asUInt)
TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please pull your source, recompile tools and recreate maps using the updated mapextractor, then replace your old map files with new files. If you still have problems search on forum for error TCE00018.",
fileName.c_str(), 4, header.mapMagic.asChar, 4, header.versionMagic.asChar, 4, MapMagic.asChar, 4, MapVersionMagic.asChar);
else
ret = true;
}
fclose(file);
}
return ret;
}
bool Map::ExistVMap(uint32 mapid, int gx, int gy)
{
if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
{
if (vmgr->isMapLoadingEnabled())
{
VMAP::LoadResult result = vmgr->existsMap((sWorld->GetDataPath() + "vmaps").c_str(), mapid, gx, gy);
std::string name = vmgr->getDirFileName(mapid, gx, gy);
switch (result)
{
case VMAP::LoadResult::Success:
break;
case VMAP::LoadResult::FileNotFound:
TC_LOG_ERROR("maps", "VMap file '%s' does not exist", (sWorld->GetDataPath() + "vmaps/" + name).c_str());
TC_LOG_ERROR("maps", "Please place VMAP files (*.vmtree and *.vmtile) in the vmap directory (%s), or correct the DataDir setting in your worldserver.conf file.", (sWorld->GetDataPath() + "vmaps/").c_str());
return false;
case VMAP::LoadResult::VersionMismatch:
TC_LOG_ERROR("maps", "VMap file '%s' couldn't be loaded", (sWorld->GetDataPath() + "vmaps/" + name).c_str());
TC_LOG_ERROR("maps", "This is because the version of the VMap file and the version of this module are different, please re-extract the maps with the tools compiled with this module.");
return false;
}
}
}
return true;
}
void Map::LoadMMap(int gx, int gy)
{
if (!DisableMgr::IsPathfindingEnabled(GetId()))
return;
bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap(sWorld->GetDataPath(), GetId(), gx, gy);
if (mmapLoadResult)
TC_LOG_DEBUG("mmaps", "MMAP loaded name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
else
TC_LOG_ERROR("mmaps", "Could not load MMAP name:%s, id:%d, x:%d, y:%d (mmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
}
void Map::LoadVMap(int gx, int gy)
{
if (!VMAP::VMapFactory::createOrGetVMapManager()->isMapLoadingEnabled())
return;
// 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:
TC_LOG_DEBUG("maps", "VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
break;
case VMAP::VMAP_LOAD_RESULT_ERROR:
TC_LOG_ERROR("maps", "Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
break;
case VMAP::VMAP_LOAD_RESULT_IGNORED:
TC_LOG_DEBUG("maps", "Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), gx, gy, gx, gy);
break;
}
}
void Map::LoadMap(int gx, int gy)
{
LoadMapImpl(this, gx, gy);
for (Map* childBaseMap : *m_childTerrainMaps)
childBaseMap->LoadMap(gx, gy);
}
void Map::LoadMapImpl(Map* map, int gx, int gy)
{
if (map->GridMaps[gx][gy])
return;
Map* parent = map->m_parentMap;
++parent->GridMapReference[gx][gy];
// load grid map for base map
if (parent != map)
{
GridCoord ngridCoord = GridCoord((MAX_NUMBER_OF_GRIDS - 1) - gx, (MAX_NUMBER_OF_GRIDS - 1) - gy);
if (!parent->GridMaps[gx][gy])
parent->EnsureGridCreated(ngridCoord);
map->GridMaps[gx][gy] = parent->GridMaps[gx][gy];
return;
}
// map file name
std::string fileName = Trinity::StringFormat("%smaps/%04u_%02u_%02u.map", sWorld->GetDataPath().c_str(), map->GetId(), gx, gy);
TC_LOG_DEBUG("maps", "Loading map %s", fileName.c_str());
// loading data
map->GridMaps[gx][gy] = new GridMap();
if (!map->GridMaps[gx][gy]->loadData(fileName.c_str()))
TC_LOG_ERROR("maps", "Error loading map file: %s", fileName.c_str());
sScriptMgr->OnLoadGridMap(map, map->GridMaps[gx][gy], gx, gy);
}
void Map::UnloadMap(int gx, int gy)
{
for (Map* childBaseMap : *m_childTerrainMaps)
childBaseMap->UnloadMap(gx, gy);
UnloadMapImpl(this, gx, gy);
}
void Map::UnloadMapImpl(Map* map, int gx, int gy)
{
if (map->GridMaps[gx][gy])
{
Map* parent = map->m_parentMap;
if (!--parent->GridMapReference[gx][gy])
{
parent->GridMaps[gx][gy]->unloadData();
delete parent->GridMaps[gx][gy];
parent->GridMaps[gx][gy] = nullptr;
}
}
map->GridMaps[gx][gy] = nullptr;
}
void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
LoadVMap(gx, gy);
LoadMMap(gx, gy);
}
void Map::LoadAllCells()
{
for (uint32 cellX = 0; cellX < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellX++)
for (uint32 cellY = 0; cellY < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellY++)
LoadGrid((cellX + 0.5f - CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL, (cellY + 0.5f - CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL);
}
void Map::InitStateMachine()
{
si_GridStates[GRID_STATE_INVALID] = new InvalidState();
si_GridStates[GRID_STATE_ACTIVE] = new ActiveState();
si_GridStates[GRID_STATE_IDLE] = new IdleState();
si_GridStates[GRID_STATE_REMOVAL] = new RemovalState();
}
void Map::DeleteStateMachine()
{
delete si_GridStates[GRID_STATE_INVALID];
delete si_GridStates[GRID_STATE_ACTIVE];
delete si_GridStates[GRID_STATE_IDLE];
delete si_GridStates[GRID_STATE_REMOVAL];
}
Map::Map(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode, Map* _parent):
_creatureToMoveLock(false), _gameObjectsToMoveLock(false), _dynamicObjectsToMoveLock(false), _areaTriggersToMoveLock(false),
i_mapEntry(sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId),
m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE),
m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD),
m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()),
i_gridExpiry(expiry),
i_scriptLock(false), _defaultLight(DB2Manager::GetDefaultMapLight(id))
{
if (_parent)
{
m_parentMap = _parent;
m_parentTerrainMap = m_parentMap->m_parentTerrainMap;
m_childTerrainMaps = m_parentMap->m_childTerrainMaps;
}
else
{
m_parentMap = this;
m_parentTerrainMap = this;
m_childTerrainMaps = new std::vector