/* * Copyright (C) 2005-2012 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "MapManager.h" #include "Log.h" #include "GridStates.h" #include "CellImpl.h" #include "Map.h" #include "DBCEnums.h" #include "DBCStores.h" #include "GridMap.h" #include "VMapFactory.h" #include "MoveMap.h" #include "World.h" #include "Policies/SingletonImp.h" #include "Util.h" char const* MAP_MAGIC = "MAPS"; char const* MAP_VERSION_MAGIC = "v1.2"; char const* MAP_AREA_MAGIC = "AREA"; char const* MAP_HEIGHT_MAGIC = "MHGT"; char const* MAP_LIQUID_MAGIC = "MLIQ"; GridMap::GridMap() { m_flags = 0; // Area data m_gridArea = 0; m_area_map = NULL; // Height level data m_gridHeight = INVALID_HEIGHT_VALUE; m_gridGetHeight = &GridMap::getHeightFromFlat; m_V9 = NULL; m_V8 = NULL; // Liquid data m_liquidType = 0; m_liquid_offX = 0; m_liquid_offY = 0; m_liquid_width = 0; m_liquid_height = 0; m_liquidLevel = INVALID_HEIGHT_VALUE; m_liquid_type = NULL; m_liquid_map = NULL; } GridMap::~GridMap() { unloadData(); } bool GridMap::loadData(char* filename) { // Unload old data if exist unloadData(); GridMapFileHeader header; // Not return error if file not found FILE* in = fopen(filename, "rb"); if (!in) return true; fread(&header, sizeof(header), 1, in); if (header.mapMagic == *((uint32 const*)(MAP_MAGIC)) && header.versionMagic == *((uint32 const*)(MAP_VERSION_MAGIC)) && IsAcceptableClientBuild(header.buildMagic)) { // 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 && !loadGridMapLiquidData(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 non-compatible version (outdated?). Please, create new using ad.exe program.", filename); fclose(in); return false; } void GridMap::unloadData() { if (m_area_map) delete[] m_area_map; if (m_V9) delete[] m_V9; if (m_V8) delete[] m_V8; if (m_liquid_type) delete[] m_liquid_type; if (m_liquid_map) delete[] m_liquid_map; m_area_map = NULL; m_V9 = NULL; m_V8 = NULL; m_liquid_type = NULL; m_liquid_map = NULL; m_gridGetHeight = &GridMap::getHeightFromFlat; } bool GridMap::loadAreaData(FILE* in, uint32 offset, uint32 /*size*/) { GridMapAreaHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); if (header.fourcc != *((uint32 const*)(MAP_AREA_MAGIC))) return false; m_gridArea = header.gridArea; if (!(header.flags & MAP_AREA_NO_AREA)) { m_area_map = new uint16 [16 * 16]; fread(m_area_map, sizeof(uint16), 16 * 16, in); } return true; } bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/) { GridMapHeightHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); if (header.fourcc != *((uint32 const*)(MAP_HEIGHT_MAGIC))) return false; m_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]; fread(m_uint16_V9, sizeof(uint16), 129 * 129, in); fread(m_uint16_V8, sizeof(uint16), 128 * 128, in); m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535; m_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]; fread(m_uint8_V9, sizeof(uint8), 129 * 129, in); fread(m_uint8_V8, sizeof(uint8), 128 * 128, in); m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 255; m_gridGetHeight = &GridMap::getHeightFromUint8; } else { m_V9 = new float [129 * 129]; m_V8 = new float [128 * 128]; fread(m_V9, sizeof(float), 129 * 129, in); fread(m_V8, sizeof(float), 128 * 128, in); m_gridGetHeight = &GridMap::getHeightFromFloat; } } else m_gridGetHeight = &GridMap::getHeightFromFlat; return true; } bool GridMap::loadGridMapLiquidData(FILE* in, uint32 offset, uint32 /*size*/) { GridMapLiquidHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); if (header.fourcc != *((uint32 const*)(MAP_LIQUID_MAGIC))) return false; m_liquidType = header.liquidType; m_liquid_offX = header.offsetX; m_liquid_offY = header.offsetY; m_liquid_width = header.width; m_liquid_height = header.height; m_liquidLevel = header.liquidLevel; if (!(header.flags & MAP_LIQUID_NO_TYPE)) { m_liquid_type = new uint8 [16 * 16]; fread(m_liquid_type, sizeof(uint8), 16 * 16, in); } if (!(header.flags & MAP_LIQUID_NO_HEIGHT)) { m_liquid_map = new float [m_liquid_width * m_liquid_height]; fread(m_liquid_map, sizeof(float), m_liquid_width * m_liquid_height, in); } return true; } uint16 GridMap::getArea(float x, float y) { if (!m_area_map) return m_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 m_area_map[lx * 16 + ly]; } float GridMap::getHeightFromFlat(float /*x*/, float /*y*/) const { return m_gridHeight; } float GridMap::getHeightFromFloat(float x, float y) const { if (!m_V8 || !m_V9) return m_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 m_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) * m_gridIntHeightMultiplier + m_gridHeight; } float GridMap::getHeightFromUint16(float x, float y) const { if (!m_uint16_V8 || !m_uint16_V9) return m_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) * m_gridIntHeightMultiplier + m_gridHeight; } float GridMap::getLiquidLevel(float x, float y) { if (!m_liquid_map) return m_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)) - m_liquid_offY; int cy_int = ((int)y & (MAP_RESOLUTION - 1)) - m_liquid_offX; if (cx_int < 0 || cx_int >= m_liquid_height) return INVALID_HEIGHT_VALUE; if (cy_int < 0 || cy_int >= m_liquid_width) return INVALID_HEIGHT_VALUE; return m_liquid_map[cx_int * m_liquid_width + cy_int]; } uint8 GridMap::getTerrainType(float x, float y) { if (!m_liquid_type) return (uint8)m_liquidType; 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 m_liquid_type[lx * 16 + ly]; } // Get water state on map GridMapLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData* data) { // Check water type (if no water return) if (!m_liquid_type && !m_liquidType) 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 uint8 type = m_liquid_type ? m_liquid_type[(x_int >> 3) * 16 + (y_int >> 3)] : m_liquidType; 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 - m_liquid_offY; if (lx_int < 0 || lx_int >= m_liquid_height) return LIQUID_MAP_NO_WATER; int ly_int = y_int - m_liquid_offX; if (ly_int < 0 || ly_int >= m_liquid_width) return LIQUID_MAP_NO_WATER; // Get water level float liquid_level = m_liquid_map ? m_liquid_map[lx_int * m_liquid_width + ly_int] : m_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->type = type; data->level = liquid_level; data->depth_level = ground_level; } // For speed check as int values int delta = int((liquid_level - z) * 10); // Get position delta if (delta > 20) // Under water return LIQUID_MAP_UNDER_WATER; if (delta > 0) // In water return LIQUID_MAP_IN_WATER; if (delta > -1) // Walk on water return LIQUID_MAP_WATER_WALK; // Above water return LIQUID_MAP_ABOVE_WATER; } bool GridMap::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); FILE* pf = fopen(tmp, "rb"); if (!pf) { sLog.outError("Check existing of map file '%s': not exist!", tmp); delete[] tmp; return false; } GridMapFileHeader header; fread(&header, sizeof(header), 1, pf); if (header.mapMagic != *((uint32 const*)(MAP_MAGIC)) || header.versionMagic != *((uint32 const*)(MAP_VERSION_MAGIC)) || !IsAcceptableClientBuild(header.buildMagic)) { sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.", tmp); delete [] tmp; fclose(pf); //close file before return return false; } delete [] tmp; fclose(pf); return true; } bool GridMap::ExistVMap(uint32 mapid, int gx, int gy) { if (VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager()) { if (vmgr->isMapLoadingEnabled()) { // x and y are swapped !! => fixed now 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 point to wrong version vmap file, redo vmaps with latest vmap_assembler.exe program", (sWorld.GetDataPath() + "vmaps/" + name).c_str()); return false; } } } return true; } ////////////////////////////////////////////////////////////////////////// TerrainInfo::TerrainInfo(uint32 mapid) : m_mapId(mapid) { for (int k = 0; k < MAX_NUMBER_OF_GRIDS; ++k) { for (int i = 0; i < MAX_NUMBER_OF_GRIDS; ++i) { m_GridMaps[i][k] = NULL; m_GridRef[i][k] = 0; } } //clean up GridMap objects every minute const uint32 iCleanUpInterval = 60; //schedule start randlomly const uint32 iRandomStart = urand(20, 40); i_timer.SetInterval(iCleanUpInterval * 1000); i_timer.SetCurrent(iRandomStart * 1000); } TerrainInfo::~TerrainInfo() { for (int k = 0; k < MAX_NUMBER_OF_GRIDS; ++k) for (int i = 0; i < MAX_NUMBER_OF_GRIDS; ++i) delete m_GridMaps[i][k]; VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(m_mapId); MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(m_mapId); } GridMap* TerrainInfo::Load(const uint32 x, const uint32 y) { MANGOS_ASSERT(x < MAX_NUMBER_OF_GRIDS); MANGOS_ASSERT(y < MAX_NUMBER_OF_GRIDS); //reference grid as a first step RefGrid(x, y); //quick check if GridMap already loaded GridMap* pMap = m_GridMaps[x][y]; if (!pMap) pMap = LoadMapAndVMap(x, y); return pMap; } //schedule lazy GridMap object cleanup void TerrainInfo::Unload(const uint32 x, const uint32 y) { MANGOS_ASSERT(x < MAX_NUMBER_OF_GRIDS); MANGOS_ASSERT(y < MAX_NUMBER_OF_GRIDS); if (m_GridMaps[x][y]) { //decrease grid reference count... if (UnrefGrid(x, y) == 0) { //TODO: add your additional logic here } } } //call this method only void TerrainInfo::CleanUpGrids(const uint32 diff) { i_timer.Update(diff); if (!i_timer.Passed()) return; for (int y = 0; y < MAX_NUMBER_OF_GRIDS; ++y) { for (int x = 0; x < MAX_NUMBER_OF_GRIDS; ++x) { const int16& iRef = m_GridRef[x][y]; GridMap* pMap = m_GridMaps[x][y]; //delete those GridMap objects which have refcount = 0 if (pMap && iRef == 0) { m_GridMaps[x][y] = NULL; //delete grid data if reference count == 0 pMap->unloadData(); delete pMap; //unload VMAPS... VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(m_mapId, x, y); //unload mmap... MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(m_mapId, x, y); } } } i_timer.Reset(); } int TerrainInfo::RefGrid(const uint32& x, const uint32& y) { MANGOS_ASSERT(x < MAX_NUMBER_OF_GRIDS); MANGOS_ASSERT(y < MAX_NUMBER_OF_GRIDS); LOCK_GUARD _lock(m_refMutex); return (m_GridRef[x][y] += 1); } int TerrainInfo::UnrefGrid(const uint32& x, const uint32& y) { MANGOS_ASSERT(x < MAX_NUMBER_OF_GRIDS); MANGOS_ASSERT(y < MAX_NUMBER_OF_GRIDS); int16& iRef = m_GridRef[x][y]; LOCK_GUARD _lock(m_refMutex); if (iRef > 0) return (iRef -= 1); return 0; } float TerrainInfo::GetHeight(float x, float y, float z, bool pUseVmaps, float maxSearchDist) const { // find raw .map surface under Z coordinates float mapHeight; float z2 = z + 2.f; if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { float _mapheight = gmap->getHeight(x, y); // look from a bit higher pos to find the floor, ignore under surface case if (z2 > _mapheight) mapHeight = _mapheight; else mapHeight = VMAP_INVALID_HEIGHT_VALUE; } else mapHeight = VMAP_INVALID_HEIGHT_VALUE; float vmapHeight; if (pUseVmaps) { VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); if (vmgr->isHeightCalcEnabled()) { // if mapHeight has been found search vmap height at least until mapHeight point // this prevent case when original Z "too high above ground and vmap height search fail" // this will not affect most normal cases (no map in instance, or stay at ground at continent) if (mapHeight > INVALID_HEIGHT && z2 - mapHeight > maxSearchDist) maxSearchDist = z2 - mapHeight + 1.0f; // 1.0 make sure that we not fail for case when map height near but above for vamp height // look from a bit higher pos to find the floor vmapHeight = vmgr->getHeight(GetMapId(), x, y, z2, maxSearchDist); } else vmapHeight = VMAP_INVALID_HEIGHT_VALUE; } else vmapHeight = VMAP_INVALID_HEIGHT_VALUE; // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT // vmapheight set for any under Z value or <= INVALID_HEIGHT if (vmapHeight > INVALID_HEIGHT) { if (mapHeight > INVALID_HEIGHT) { // we have mapheight and vmapheight and must select more appropriate // we are already under the surface or vmap height above map heigt // or if the distance of the vmap height is less the land height distance if (z < mapHeight || vmapHeight > mapHeight || fabs(mapHeight - z) > fabs(vmapHeight - z)) return vmapHeight; else return mapHeight; // better use .map surface height } else return vmapHeight; // we have only vmapHeight (if have) } return mapHeight; } 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 TerrainInfo::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) { DEBUG_LOG("Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId); atEntry = GetAreaEntryByAreaID(wmoEntry->areaId); } return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); } bool TerrainInfo::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(GetMapId(), x, y, vmap_z, flags, adtId, rootId, groupId)) { // check if there's terrain between player height and object height if (GridMap* gmap = const_cast(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; } uint16 TerrainInfo::GetAreaFlag(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 = GetAreaEntryByAreaID(wmoEntry->areaId); } uint16 areaflag; if (atEntry) areaflag = atEntry->exploreFlag; else { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) areaflag = gmap->getArea(x, y); // this used while not all *.map files generated (instances) else areaflag = GetAreaFlagByMapId(GetMapId()); } if (isOutdoors) { if (haveAreaInfo) *isOutdoors = IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); else *isOutdoors = true; } return areaflag; } uint8 TerrainInfo::GetTerrainType(float x, float y) const { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) return gmap->getTerrainType(x, y); else return 0; } uint32 TerrainInfo::GetAreaId(float x, float y, float z) const { return TerrainManager::GetAreaIdByAreaFlag(GetAreaFlag(x, y, z), m_mapId); } uint32 TerrainInfo::GetZoneId(float x, float y, float z) const { return TerrainManager::GetZoneIdByAreaFlag(GetAreaFlag(x, y, z), m_mapId); } void TerrainInfo::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const { TerrainManager::GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(x, y, z), m_mapId); } GridMapLiquidStatus TerrainInfo::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData* data) const { GridMapLiquidStatus result = LIQUID_MAP_NO_WATER; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); float liquid_level, ground_level = INVALID_HEIGHT_VALUE; uint32 liquid_type; if (vmgr->GetLiquidLevel(GetMapId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type)) { DEBUG_LOG("getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type); // Check water level and ground level if (liquid_level > ground_level && z > ground_level - 2) { // All ok in water -> store data if (data) { data->type = liquid_type; data->level = liquid_level; data->depth_level = ground_level; } // For speed check as int values int delta = int((liquid_level - z) * 10); // Get position delta if (delta > 20) // Under water return LIQUID_MAP_UNDER_WATER; if (delta > 0) // In water return LIQUID_MAP_IN_WATER; if (delta > -1) // Walk on water return LIQUID_MAP_WATER_WALK; result = LIQUID_MAP_ABOVE_WATER; } } if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) { GridMapLiquidData map_data; GridMapLiquidStatus 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) *data = map_data; return map_result; } } return result; } bool TerrainInfo::IsInWater(float x, float y, float pZ, GridMapLiquidData* data) const { // Check surface in x, y point for liquid if (const_cast(this)->GetGrid(x, y)) { GridMapLiquidData liquid_status; GridMapLiquidData* liquid_ptr = data ? data : &liquid_status; if (getLiquidStatus(x, y, pZ, MAP_ALL_LIQUIDS, liquid_ptr)) { //if (liquid_prt->level - liquid_prt->depth_level > 2) //??? return true; } } return false; } bool TerrainInfo::IsUnderWater(float x, float y, float z) const { if (const_cast(this)->GetGrid(x, y)) { if (getLiquidStatus(x, y, z, MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN)&LIQUID_MAP_UNDER_WATER) return true; } return false; } /** * Function find higher form water or ground height for current floor * * @param x, y, z Coordinates original point at floor level * * @param pGround optional arg for retrun calculated by function work ground height, it let avoid in caller code recalculate height for point if it need * * @param swim z coordinate can be calculated for select above/at or under z coordinate (for fly or swim/walking by bottom) * in last cases for in water returned under water height for avoid client set swimming unit as saty at water. * * @return calculated z coordinate */ float TerrainInfo::GetWaterOrGroundLevel(float x, float y, float z, float* pGround /*= NULL*/, bool swim /*= false*/) const { if (const_cast(this)->GetGrid(x, y)) { // we need ground level (including grid height version) for proper return water level in point float ground_z = GetHeight(x, y, z, true, DEFAULT_WATER_SEARCH); if (pGround) *pGround = ground_z; GridMapLiquidData liquid_status; GridMapLiquidStatus res = getLiquidStatus(x, y, ground_z, MAP_ALL_LIQUIDS, &liquid_status); return res ? (swim ? liquid_status.level - 2.0f : liquid_status.level) : ground_z; } return VMAP_INVALID_HEIGHT_VALUE; } GridMap* TerrainInfo::GetGrid(const float x, const 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 //quick check if GridMap already loaded GridMap* pMap = m_GridMaps[gx][gy]; if (!pMap) pMap = LoadMapAndVMap(gx, gy); return pMap; } GridMap* TerrainInfo::LoadMapAndVMap(const uint32 x, const uint32 y) { //double checked lock pattern if (!m_GridMaps[x][y]) { LOCK_GUARD lock(m_mutex); if (!m_GridMaps[x][y]) { GridMap* map = new GridMap(); // 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(), m_mapId, x, y); sLog.outDetail("Loading map %s", tmp); if (!map->loadData(tmp)) { sLog.outError("Error load map file: \n %s\n", tmp); //ASSERT(false); } delete [] tmp; m_GridMaps[x][y] = map; //load VMAPs for current map/grid... const MapEntry* i_mapEntry = sMapStore.LookupEntry(m_mapId); const char* mapName = i_mapEntry ? i_mapEntry->name[sWorld.GetDefaultDbcLocale()] : "UNNAMEDMAP\x0"; int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath() + "vmaps").c_str(), m_mapId, x, y); switch (vmapLoadResult) { case VMAP::VMAP_LOAD_RESULT_OK: sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", mapName, m_mapId, x, y, x, y); break; case VMAP::VMAP_LOAD_RESULT_ERROR: sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", mapName, m_mapId, x, y, x, y); break; case VMAP::VMAP_LOAD_RESULT_IGNORED: DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", mapName, m_mapId, x, y, x, y); break; } // load navmesh MMAP::MMapFactory::createOrGetMMapManager()->loadMap(m_mapId, x, y); } } return m_GridMaps[x][y]; } float TerrainInfo::GetWaterLevel(float x, float y, float z, float* pGround /*= NULL*/) const { if (const_cast(this)->GetGrid(x, y)) { // we need ground level (including grid height version) for proper return water level in point float ground_z = GetHeight(x, y, z, true, DEFAULT_WATER_SEARCH); if (pGround) *pGround = ground_z; GridMapLiquidData liquid_status; GridMapLiquidStatus res = getLiquidStatus(x, y, ground_z, MAP_ALL_LIQUIDS, &liquid_status); if (!res) return VMAP_INVALID_HEIGHT_VALUE; return liquid_status.level; } return VMAP_INVALID_HEIGHT_VALUE; } ////////////////////////////////////////////////////////////////////////// #define CLASS_LOCK MaNGOS::ClassLevelLockable INSTANTIATE_SINGLETON_2(TerrainManager, CLASS_LOCK); INSTANTIATE_CLASS_MUTEX(TerrainManager, ACE_Thread_Mutex); TerrainManager::TerrainManager() { } TerrainManager::~TerrainManager() { for (TerrainDataMap::iterator it = i_TerrainMap.begin(); it != i_TerrainMap.end(); ++it) delete it->second; } TerrainInfo* TerrainManager::LoadTerrain(const uint32 mapId) { Guard _guard(*this); TerrainInfo* ptr = NULL; TerrainDataMap::const_iterator iter = i_TerrainMap.find(mapId); if (iter == i_TerrainMap.end()) { ptr = new TerrainInfo(mapId); i_TerrainMap[mapId] = ptr; } else ptr = (*iter).second; return ptr; } void TerrainManager::UnloadTerrain(const uint32 mapId) { if (sWorld.getConfig(CONFIG_BOOL_GRID_UNLOAD) == 0) return; Guard _guard(*this); TerrainDataMap::iterator iter = i_TerrainMap.find(mapId); if (iter != i_TerrainMap.end()) { TerrainInfo* ptr = (*iter).second; //lets check if this object can be actually freed if (ptr->IsReferenced() == false) { i_TerrainMap.erase(iter); delete ptr; } } } void TerrainManager::Update(const uint32 diff) { //global garbage collection for GridMap objects and VMaps for (TerrainDataMap::iterator iter = i_TerrainMap.begin(); iter != i_TerrainMap.end(); ++iter) iter->second->CleanUpGrids(diff); } void TerrainManager::UnloadAll() { for (TerrainDataMap::iterator it = i_TerrainMap.begin(); it != i_TerrainMap.end(); ++it) delete it->second; i_TerrainMap.clear(); } uint32 TerrainManager::GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id) { AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); if (entry) return entry->ID; else return 0; } uint32 TerrainManager::GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id) { AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); if (entry) return (entry->zone != 0) ? entry->zone : entry->ID; else return 0; } void TerrainManager::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id) { AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); areaid = entry ? entry->ID : 0; zoneid = entry ? ((entry->zone != 0) ? entry->zone : entry->ID) : 0; }