/* * Copyright (C) 2005-2010 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 "Config/ConfigEnv.h" #include "DBCEnums.h" #include "DBCStores.h" #include "GridMap.h" #include "VMapFactory.h" #include "World.h" char const* MAP_MAGIC = "MAPS"; char const* MAP_VERSION_MAGIC = "v1.1"; 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; 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; 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; if (cy_int < 0 || cy_int >=m_liquid_width ) return INVALID_HEIGHT; 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; }