/*
* Copyright (C) 2008-2018 TrinityCore
* Copyright (C) 2005-2009 MaNGOS
*
* 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','.','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'} };
#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())
{
bool exists = vmgr->existsMap((sWorld->GetDataPath()+ "vmaps").c_str(), mapid, gx, gy);
if (!exists)
{
std::string name = vmgr->getDirFileName(mapid, gx, gy);
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;
}
}
}
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, bool reload)
{
LoadMapImpl(this, gx, gy, reload);
for (Map* childBaseMap : *m_childTerrainMaps)
LoadMapImpl(childBaseMap, gx, gy, reload);
}
void Map::LoadMapImpl(Map* map, int gx, int gy, bool reload)
{
if (map->i_InstanceId != 0)
{
if (map->GridMaps[gx][gy])
return;
// load grid map for base map
if (!map->m_parentMap->GridMaps[gx][gy])
map->m_parentMap->EnsureGridCreated(GridCoord((MAX_NUMBER_OF_GRIDS - 1) - gx, (MAX_NUMBER_OF_GRIDS - 1) - gy));
map->m_parentMap->ToMapInstanced()->AddGridMapReference(GridCoord(gx, gy));
map->GridMaps[gx][gy] = map->m_parentMap->GridMaps[gx][gy];
return;
}
if (map->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 (map->GridMaps[gx][gy])
{
TC_LOG_DEBUG("maps", "Unloading previously loaded map %u before reloading.", map->GetId());
sScriptMgr->OnUnloadGridMap(map, map->GridMaps[gx][gy], gx, gy);
delete map->GridMaps[gx][gy];
map->GridMaps[gx][gy] = nullptr;
}
// 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(Map* map, int gx, int gy)
{
if (map->i_InstanceId == 0)
{
if (map->GridMaps[gx][gy])
{
map->GridMaps[gx][gy]->unloadData();
delete map->GridMaps[gx][gy];
}
}
else
static_cast(map->m_parentMap)->RemoveGridMapReference(GridCoord(gx, gy));
map->GridMaps[gx][gy] = nullptr;
}
void Map::LoadMapAndVMap(int gx, int gy)
{
LoadMap(gx, gy);
// Only load the data for the base map
if (i_InstanceId == 0)
{
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, uint8 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_childTerrainMaps = m_parentMap->m_childTerrainMaps;
}
else
{
m_parentMap = this;
m_childTerrainMaps = new std::vector