diff options
Diffstat (limited to 'src/server')
25 files changed, 1886 insertions, 1628 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index d4c8b8aec40..52349b9f749 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -3375,7 +3375,7 @@ void WorldObject::MovePositionToFirstCollision(Position &pos, float dist, float // Unit is flying, check for potential collision via vmaps if (path.GetPathType() & PATHFIND_NOT_USING_PATH) { - col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(PhasingHandler::GetTerrainMapId(GetPhaseShift(), GetMap(), pos.m_positionX, pos.m_positionY), + col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(PhasingHandler::GetTerrainMapId(GetPhaseShift(), GetMap()->GetTerrain(), pos.m_positionX, pos.m_positionY), pos.m_positionX, pos.m_positionY, pos.m_positionZ + halfHeight, destx, desty, destz + halfHeight, destx, desty, destz, -0.5f); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6d428abf54e..4bc63a2c304 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -114,6 +114,7 @@ #include "SpellPackets.h" #include "StringConvert.h" #include "TalentPackets.h" +#include "TerrainMgr.h" #include "ToyPackets.h" #include "TradeData.h" #include "Transport.h" @@ -7182,7 +7183,7 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid) if (!sMapStore.LookupEntry(map)) return 0; - zone = sMapMgr->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), map, posx, posy, posz); + zone = sTerrainMgr.GetZoneId(PhasingHandler::GetEmptyPhaseShift(), map, posx, posy, posz); if (zone > 0) { @@ -19759,7 +19760,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) if (!createPosition.TransportGuid) { m_homebind.WorldRelocate(createPosition.Loc); - m_homebindAreaId = sMapMgr->GetAreaId(PhasingHandler::GetEmptyPhaseShift(), m_homebind); + m_homebindAreaId = sTerrainMgr.GetAreaId(PhasingHandler::GetEmptyPhaseShift(), m_homebind); saveHomebindToDb(); ok = true; @@ -19775,7 +19776,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) ASSERT(loc, "Missing fallback graveyard location for faction %u", uint32(GetTeamId())); m_homebind.WorldRelocate(loc->Loc); - m_homebindAreaId = sMapMgr->GetAreaId(PhasingHandler::GetEmptyPhaseShift(), loc->Loc); + m_homebindAreaId = sTerrainMgr.GetAreaId(PhasingHandler::GetEmptyPhaseShift(), loc->Loc); saveHomebindToDb(); } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 844a800f242..140e1c5a6cd 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -60,6 +60,7 @@ #include "SpellScript.h" #include "StringConvert.h" #include "TemporarySummon.h" +#include "TerrainMgr.h" #include "Timer.h" #include "TransportMgr.h" #include "Vehicle.h" @@ -2430,7 +2431,7 @@ void ObjectMgr::LoadCreatures() uint32 zoneId = 0; uint32 areaId = 0; PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap); - sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint); + sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA); @@ -2735,7 +2736,7 @@ void ObjectMgr::LoadGameObjects() uint32 zoneId = 0; uint32 areaId = 0; PhasingHandler::InitDbVisibleMapId(phaseShift, data.terrainSwapMap); - sMapMgr->GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint); + sTerrainMgr.GetZoneAndAreaId(phaseShift, zoneId, areaId, data.mapId, data.spawnPoint); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA); @@ -6949,7 +6950,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveyard(WorldLocation const& lo uint32 MapId = location.GetMapId(); // search for zone associated closest graveyard - uint32 zoneId = sMapMgr->GetZoneId(conditionObject ? conditionObject->GetPhaseShift() : PhasingHandler::GetEmptyPhaseShift(), MapId, x, y, z); + uint32 zoneId = sTerrainMgr.GetZoneId(conditionObject ? conditionObject->GetPhaseShift() : PhasingHandler::GetEmptyPhaseShift(), MapId, x, y, z); if (!zoneId) { diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 3fd667dd718..d9d13e562bf 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -57,6 +57,11 @@ class Conversation; #define MAP_SIZE (SIZE_OF_GRIDS*MAX_NUMBER_OF_GRIDS) #define MAP_HALFSIZE (MAP_SIZE/2) +#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface +#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE +#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT +#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations + // Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case) typedef TYPELIST_4(Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes; typedef TYPELIST_7(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, SceneObject, Conversation) AllGridObjectTypes; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 4c5fca4ff47..91acaad9a0e 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -38,6 +38,7 @@ #include "PhasingHandler.h" #include "Player.h" #include "Random.h" +#include "TerrainMgr.h" #include "UpdateData.h" #include "Util.h" #include "World.h" @@ -2179,7 +2180,7 @@ void Group::ResetInstances(uint8 method, bool isRaid, bool isLegacy, Player* Sen WorldSafeLocsEntry const* graveyardLocation = sObjectMgr->GetClosestGraveyard( WorldLocation(instanceEntrance->target_mapId, instanceEntrance->target_X, instanceEntrance->target_Y, instanceEntrance->target_Z), SendMsgTo->GetTeam(), nullptr); - uint32 const zoneId = sMapMgr->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), graveyardLocation->Loc.GetMapId(), + uint32 const zoneId = sTerrainMgr.GetZoneId(PhasingHandler::GetEmptyPhaseShift(), graveyardLocation->Loc.GetMapId(), graveyardLocation->Loc.GetPositionX(), graveyardLocation->Loc.GetPositionY(), graveyardLocation->Loc.GetPositionZ()); for (MemberSlot const& member : GetMemberSlots()) diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 1628cdc0850..bc117d6e305 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -23,13 +23,13 @@ #include "GameTime.h" #include "Item.h" #include "Log.h" -#include "MapManager.h" #include "NPCHandler.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" #include "QueryPackets.h" #include "Realm.h" +#include "TerrainMgr.h" #include "World.h" void WorldSession::BuildNameQueryData(ObjectGuid guid, WorldPackets::Query::NameCacheLookupResult& lookupData) @@ -146,12 +146,12 @@ void WorldSession::HandleQueryCorpseLocation(WorldPackets::Query::QueryCorpseLoc if (corpseMapEntry->IsDungeon() && corpseMapEntry->CorpseMapID >= 0) { // if corpse map have entrance - if (Map* entranceMap = sMapMgr->CreateBaseMap(corpseMapEntry->CorpseMapID)) + if (std::shared_ptr<TerrainInfo> entranceTerrain = sTerrainMgr.LoadTerrain(corpseMapEntry->CorpseMapID)) { mapID = corpseMapEntry->CorpseMapID; x = corpseMapEntry->Corpse.X; y = corpseMapEntry->Corpse.Y; - z = entranceMap->GetHeight(player->GetPhaseShift(), x, y, MAX_HEIGHT); + z = entranceTerrain->GetStaticHeight(player->GetPhaseShift(), x, y, MAX_HEIGHT); } } } diff --git a/src/server/game/Maps/GridMap.cpp b/src/server/game/Maps/GridMap.cpp new file mode 100644 index 00000000000..5ffca166bba --- /dev/null +++ b/src/server/game/Maps/GridMap.cpp @@ -0,0 +1,686 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "GridMap.h" +#include "DB2Stores.h" +#include "GridDefines.h" +#include "Log.h" +#include <G3D/Plane.h> +#include <G3D/Ray.h> + +// ***************************** +// 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; + _minHeightPlanes = nullptr; + // Liquid data + _liquidGlobalEntry = 0; + _liquidGlobalFlags = map_liquidHeaderTypeFlags::NoWater; + _liquidOffX = 0; + _liquidOffY = 0; + _liquidWidth = 0; + _liquidHeight = 0; + _liquidLevel = INVALID_HEIGHT; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; + _holes = nullptr; +} + +GridMap::~GridMap() +{ + unloadData(); +} + +GridMap::LoadResult GridMap::loadData(char const* 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 LoadResult::FileDoesNotExist; + + if (fread(&header, sizeof(header), 1, in) != 1) + { + fclose(in); + return LoadResult::InvalidFile; + } + + if (header.mapMagic == MapMagic && header.versionMagic == MapVersionMagic) + { + // load up area data + if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize)) + { + TC_LOG_ERROR("maps", "Error loading map area data\n"); + fclose(in); + return LoadResult::InvalidFile; + } + // load up height data + if (header.heightMapOffset && !loadHeightData(in, header.heightMapOffset, header.heightMapSize)) + { + TC_LOG_ERROR("maps", "Error loading map height data\n"); + fclose(in); + return LoadResult::InvalidFile; + } + // load up liquid data + if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize)) + { + TC_LOG_ERROR("maps", "Error loading map liquids data\n"); + fclose(in); + return LoadResult::InvalidFile; + } + // loadup holes data (if any. check header.holesOffset) + if (header.holesSize && !loadHolesData(in, header.holesOffset, header.holesSize)) + { + TC_LOG_ERROR("maps", "Error loading map holes data\n"); + fclose(in); + return LoadResult::InvalidFile; + } + fclose(in); + return LoadResult::Ok; + } + + TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s v%u), %.*s v%u 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, 4, header.mapMagic.data(), header.versionMagic, 4, MapMagic.data(), MapVersionMagic); + fclose(in); + return LoadResult::InvalidFile; +} + +void GridMap::unloadData() +{ + delete[] _areaMap; + delete[] m_V9; + delete[] m_V8; + delete[] _minHeightPlanes; + delete[] _liquidEntry; + delete[] _liquidFlags; + delete[] _liquidMap; + delete[] _holes; + _areaMap = nullptr; + m_V9 = nullptr; + m_V8 = nullptr; + _minHeightPlanes = nullptr; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; + _holes = 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.areaMagic != MapAreaMagic) + return false; + + _gridArea = header.gridArea; + if (!header.flags.HasFlag(map_areaHeaderFlags::NoArea)) + { + _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.heightMagic != MapHeightMagic) + return false; + + _gridHeight = header.gridHeight; + if (!header.flags.HasFlag(map_heightHeaderFlags::NoHeight)) + { + if (header.flags.HasFlag(map_heightHeaderFlags::HeightAsInt16)) + { + 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.HasFlag(map_heightHeaderFlags::HeightAsInt8)) + { + 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.HasFlag(map_heightHeaderFlags::HasFlightBounds)) + { + std::array<int16, 9> maxHeights; + std::array<int16, 9> minHeights; + if (fread(maxHeights.data(), sizeof(int16), maxHeights.size(), in) != maxHeights.size() || + fread(minHeights.data(), sizeof(int16), minHeights.size(), in) != minHeights.size()) + return false; + + static uint32 constexpr indices[8][3] = + { + { 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 constexpr boundGridCoords[9][2] = + { + { 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 } + }; + + _minHeightPlanes = new G3D::Plane[8]; + for (uint32 quarterIndex = 0; quarterIndex < 8; ++quarterIndex) + _minHeightPlanes[quarterIndex] = G3D::Plane( + G3D::Vector3(boundGridCoords[indices[quarterIndex][0]][0], boundGridCoords[indices[quarterIndex][0]][1], minHeights[indices[quarterIndex][0]]), + G3D::Vector3(boundGridCoords[indices[quarterIndex][1]][0], boundGridCoords[indices[quarterIndex][1]][1], minHeights[indices[quarterIndex][1]]), + G3D::Vector3(boundGridCoords[indices[quarterIndex][2]][0], boundGridCoords[indices[quarterIndex][2]][1], minHeights[indices[quarterIndex][2]]) + ); + } + + 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.liquidMagic != MapLiquidMagic) + return false; + + _liquidGlobalEntry = header.liquidType; + _liquidGlobalFlags = header.liquidFlags; + _liquidOffX = header.offsetX; + _liquidOffY = header.offsetY; + _liquidWidth = header.width; + _liquidHeight = header.height; + _liquidLevel = header.liquidLevel; + + if (!header.flags.HasFlag(map_liquidHeaderFlags::NoType)) + { + _liquidEntry = new uint16[16*16]; + if (fread(_liquidEntry, sizeof(uint16), 16*16, in) != 16*16) + return false; + + _liquidFlags = new map_liquidHeaderTypeFlags[16*16]; + if (fread(_liquidFlags, sizeof(map_liquidHeaderTypeFlags), 16*16, in) != 16*16) + return false; + } + if (!header.flags.HasFlag(map_liquidHeaderFlags::NoHeight)) + { + _liquidMap = new float[uint32(_liquidWidth) * uint32(_liquidHeight)]; + if (fread(_liquidMap, sizeof(float), _liquidWidth*_liquidHeight, in) != (uint32(_liquidWidth) * uint32(_liquidHeight))) + return false; + } + return true; +} + +bool GridMap::loadHolesData(FILE* in, uint32 offset, uint32 /*size*/) +{ + if (fseek(in, offset, SEEK_SET) != 0) + return false; + + _holes = new uint8[16 * 16 * 8]; + if (fread(_holes, sizeof(uint8), 16 * 16 * 8, in) != 16 * 16 * 8) + return false; + + return true; +} + +uint16 GridMap::getArea(float x, float y) const +{ + if (!_areaMap) + return _gridArea; + + x = 16 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); + y = 16 * (CENTER_GRID_ID - 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 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (CENTER_GRID_ID - 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); + + if (isHole(x_int, y_int)) + return INVALID_HEIGHT; + + // 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 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (CENTER_GRID_ID - 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); + + if (isHole(x_int, y_int)) + return INVALID_HEIGHT; + + 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 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (CENTER_GRID_ID - 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); + + if (isHole(x_int, y_int)) + return INVALID_HEIGHT; + + 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; +} + +bool GridMap::isHole(int row, int col) const +{ + if (!_holes) + return false; + + int cellRow = row / 8; // 8 squares per cell + int cellCol = col / 8; + int holeRow = row % 8; + int holeCol = col % 8; + + return (_holes[cellRow * 16 * 8 + cellCol * 8 + holeRow] & (1 << holeCol)) != 0; +} + +float GridMap::getMinHeight(float x, float y) const +{ + if (!_minHeightPlanes) + return -500.0f; + + GridCoord gridCoord = Trinity::ComputeGridCoordSimple(x, y); + + int32 doubleGridX = int32(std::floor(-(x - MAP_HALFSIZE) / CENTER_GRID_OFFSET)); + int32 doubleGridY = int32(std::floor(-(y - MAP_HALFSIZE) / CENTER_GRID_OFFSET)); + + float gx = x - (int32(gridCoord.x_coord) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; + float gy = y - (int32(gridCoord.y_coord) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; + + uint32 quarterIndex = 0; + if (doubleGridY & 1) + { + if (doubleGridX & 1) + quarterIndex = 4 + (gx <= gy); + else + quarterIndex = 2 + ((-SIZE_OF_GRIDS - gx) > gy); + } + else if (doubleGridX & 1) + quarterIndex = 6 + ((-SIZE_OF_GRIDS - gx) <= gy); + else + quarterIndex = gx > gy; + + G3D::Ray ray = G3D::Ray::fromOriginAndDirection(G3D::Vector3(gx, gy, 0.0f), G3D::Vector3::unitZ()); + return ray.intersection(_minHeightPlanes[quarterIndex]).z; +} + +float GridMap::getLiquidLevel(float x, float y) const +{ + if (!_liquidMap) + return _liquidLevel; + + x = MAP_RESOLUTION * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); + y = MAP_RESOLUTION * (CENTER_GRID_ID - 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]; +} + +// Get water state on map +ZLiquidStatus GridMap::GetLiquidStatus(float x, float y, float z, Optional<map_liquidHeaderTypeFlags> ReqLiquidType, LiquidData* data, float collisionHeight) const +{ + // Check water type (if no water return) + if (_liquidGlobalFlags == map_liquidHeaderTypeFlags::NoWater && !_liquidFlags) + return LIQUID_MAP_NO_WATER; + + // Get cell + float cx = MAP_RESOLUTION * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); + float cy = MAP_RESOLUTION * (CENTER_GRID_ID - 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); + map_liquidHeaderTypeFlags type = _liquidFlags ? _liquidFlags[idx] : _liquidGlobalFlags; + uint32 entry = _liquidEntry ? _liquidEntry[idx] : _liquidGlobalEntry; + if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(entry)) + { + type &= map_liquidHeaderTypeFlags::DarkWater; + uint32 liqTypeIdx = liquidEntry->SoundBank; + if (entry < 21) + { + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) + { + uint32 overrideLiquid = area->LiquidTypeID[liquidEntry->SoundBank]; + if (!overrideLiquid && area->ParentAreaID) + { + area = sAreaTableStore.LookupEntry(area->ParentAreaID); + if (area) + overrideLiquid = area->LiquidTypeID[liquidEntry->SoundBank]; + } + + if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + entry = overrideLiquid; + liqTypeIdx = liq->SoundBank; + } + } + } + + type |= map_liquidHeaderTypeFlags(1 << liqTypeIdx); + } + + if (type == map_liquidHeaderTypeFlags::NoWater) + return LIQUID_MAP_NO_WATER; + + // Check req liquid type mask + if (ReqLiquidType && (*ReqLiquidType & type) == map_liquidHeaderTypeFlags::NoWater) + 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) + 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 > collisionHeight) // 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; +} diff --git a/src/server/game/Maps/GridMap.h b/src/server/game/Maps/GridMap.h new file mode 100644 index 00000000000..e9e052e51dd --- /dev/null +++ b/src/server/game/Maps/GridMap.h @@ -0,0 +1,103 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITY_GRID_MAP_H +#define TRINITY_GRID_MAP_H + +#include "Define.h" +#include "MapDefines.h" +#include "Optional.h" +#include <cstdio> + +struct LiquidData; +enum ZLiquidStatus : uint32; +namespace G3D { class Plane; } + +class TC_GAME_API GridMap +{ + uint32 _flags; + union + { + float* m_V9; + uint16* m_uint16_V9; + uint8* m_uint8_V9; + }; + union + { + float* m_V8; + uint16* m_uint16_V8; + uint8* m_uint8_V8; + }; + G3D::Plane* _minHeightPlanes; + // Height level data + float _gridHeight; + float _gridIntHeightMultiplier; + + // Area data + uint16* _areaMap; + + // Liquid data + float _liquidLevel; + uint16* _liquidEntry; + map_liquidHeaderTypeFlags* _liquidFlags; + float* _liquidMap; + uint16 _gridArea; + uint16 _liquidGlobalEntry; + map_liquidHeaderTypeFlags _liquidGlobalFlags; + uint8 _liquidOffX; + uint8 _liquidOffY; + uint8 _liquidWidth; + uint8 _liquidHeight; + + uint8* _holes; + + bool loadAreaData(FILE* in, uint32 offset, uint32 size); + bool loadHeightData(FILE* in, uint32 offset, uint32 size); + bool loadLiquidData(FILE* in, uint32 offset, uint32 size); + bool loadHolesData(FILE* in, uint32 offset, uint32 size); + bool isHole(int row, int col) const; + + // Get height functions and pointers + typedef float (GridMap::* GetHeightPtr) (float x, float y) const; + GetHeightPtr _gridGetHeight; + float getHeightFromFloat(float x, float y) const; + float getHeightFromUint16(float x, float y) const; + float getHeightFromUint8(float x, float y) const; + float getHeightFromFlat(float x, float y) const; + +public: + GridMap(); + ~GridMap(); + + enum class LoadResult + { + Ok, + FileDoesNotExist, + InvalidFile + }; + + LoadResult loadData(char const* filename); + void unloadData(); + + uint16 getArea(float x, float y) const; + float getHeight(float x, float y) const { return (this->*_gridGetHeight)(x, y); } + float getMinHeight(float x, float y) const; + float getLiquidLevel(float x, float y) const; + ZLiquidStatus GetLiquidStatus(float x, float y, float z, Optional<map_liquidHeaderTypeFlags> ReqLiquidType, LiquidData* data = nullptr, float collisionHeight = 2.03128f) const; // DEFAULT_COLLISION_HEIGHT in Object.h +}; + +#endif // TRINITY_GRID_MAP_H diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 3c122dfd70a..440fe34bc70 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -50,6 +50,7 @@ #include "PoolMgr.h" #include "ScriptMgr.h" #include "SpellAuras.h" +#include "TerrainMgr.h" #include "Transport.h" #include "Vehicle.h" #include "VMapFactory.h" @@ -99,214 +100,9 @@ Map::~Map() sOutdoorPvPMgr->DestroyOutdoorPvPForMap(this); sBattlefieldMgr->DestroyBattlefieldsForMap(this); - if (m_parentMap == this) - delete m_childTerrainMaps; - MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId); } -void Map::DiscoverGridMapFiles() -{ - std::string tileListName = Trinity::StringFormat("%smaps/%04u.tilelist", sWorld->GetDataPath().c_str(), GetId()); - // tile list is optional - if (FILE* tileList = fopen(tileListName.c_str(), "rb")) - { - u_map_magic mapMagic = { }; - uint32 versionMagic = { }; - uint32 build; - char tilesData[MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS] = { }; - if (fread(mapMagic.data(), mapMagic.size(), 1, tileList) == 1 - && mapMagic == MapMagic - && fread(&versionMagic, sizeof(versionMagic), 1, tileList) == 1 - && versionMagic == MapVersionMagic - && fread(&build, sizeof(build), 1, tileList) == 1 - && fread(&tilesData[0], MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS, 1, tileList) == 1) - { - i_gridFileExists = std::bitset<MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS>(tilesData, std::size(tilesData)); - fclose(tileList); - return; - } - - fclose(tileList); - } - - for (uint32 gx = 0; gx < MAX_NUMBER_OF_GRIDS; ++gx) - for (uint32 gy = 0; gy < MAX_NUMBER_OF_GRIDS; ++gy) - i_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy] = ExistMap(GetId(), gx, gy, false); -} - -Map* Map::GetRootParentTerrainMap() -{ - Map* map = this; - while (map != map->m_parentTerrainMap) - map = map->m_parentTerrainMap; - - return map; -} - -bool Map::ExistMap(uint32 mapid, int gx, int gy, bool log /*= true*/) -{ - 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) - { - if (log) - { - 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 != MapMagic || header.versionMagic != MapVersionMagic) - { - if (log) - TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s v%u), %.*s v%u 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.data(), header.versionMagic, 4, MapMagic.data(), MapVersionMagic); - } - 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; - case VMAP::LoadResult::ReadFromFileFailed: - TC_LOG_ERROR("maps", "VMap file '%s' couldn't be loaded", (sWorld->GetDataPath() + "vmaps/" + name).c_str()); - TC_LOG_ERROR("maps", "This is because VMAP files are corrupted, 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.tiles", "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_WARN("mmaps.tiles", "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 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 - std::shared_ptr<GridMap> gridMap = std::make_shared<GridMap>(); - GridMap::LoadResult gridMapLoadResult = gridMap->loadData(fileName.c_str()); - if (gridMapLoadResult == GridMap::LoadResult::Ok) - map->GridMaps[gx][gy] = std::move(gridMap); - else - { - map->i_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy] = false; - Map* parentTerrain = map; - while (parentTerrain != parentTerrain->m_parentTerrainMap) - { - map->GridMaps[gx][gy] = parentTerrain->m_parentTerrainMap->GridMaps[gx][gy]; - if (map->GridMaps[gx][gy]) - break; - - parentTerrain = parentTerrain->m_parentTerrainMap; - } - } - - if (map->GridMaps[gx][gy]) - sScriptMgr->OnLoadGridMap(map, map->GridMaps[gx][gy].get(), gx, gy); - else if (gridMapLoadResult == GridMap::LoadResult::InvalidFile) - TC_LOG_ERROR("maps", "Error loading map file: %s", fileName.c_str()); -} - -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) -{ - map->GridMaps[gx][gy] = nullptr; -} - -void Map::LoadMapAndVMap(int gx, int gy) -{ - LoadMap(gx, gy); - // Only load the data for the base map - if (this == m_parentMap) - { - LoadVMap(gx, gy); - LoadMMap(gx, gy); - } -} - void Map::LoadAllCells() { for (uint32 cellX = 0; cellX < TOTAL_NUMBER_OF_CELLS_PER_MAP; cellX++) @@ -330,35 +126,20 @@ void Map::DeleteStateMachine() delete si_GridStates[GRID_STATE_REMOVAL]; } -Map::Map(uint32 id, time_t expiry, uint32 InstanceId, Difficulty SpawnMode, Map* _parent): +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_gridExpiry(expiry), m_terrain(sTerrainMgr.LoadTerrain(id)), i_scriptLock(false), _respawnCheckTimer(0) { - 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<Map*>(); - } - for (uint32 x = 0; x < MAX_NUMBER_OF_GRIDS; ++x) { for (uint32 y = 0; y < MAX_NUMBER_OF_GRIDS; ++y) { //z code - GridMaps[x][y] = nullptr; - GridMapReference[x][y] = 0; setNGrid(nullptr, x, y); } } @@ -554,23 +335,7 @@ void Map::EnsureGridCreated_i(GridCoord const& p) int gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord; int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord; - if (!GridMaps[gx][gy]) - { - Map* rootParentTerrainMap = m_parentMap->GetRootParentTerrainMap(); - // because LoadMapAndVMap is always entered using rootParentTerrainMap, we can only lock that once and not have to do it for every child map - std::unique_lock<std::mutex> lock(rootParentTerrainMap->_gridLock, std::defer_lock); - if (this != rootParentTerrainMap) - lock.lock(); - - if (!m_parentMap->GridMaps[gx][gy]) - rootParentTerrainMap->LoadMapAndVMap(gx, gy); - - if (m_parentMap->GridMaps[gx][gy]) - { - GridMaps[gx][gy] = m_parentMap->GridMaps[gx][gy]; - ++rootParentTerrainMap->GridMapReference[gx][gy]; - } - } + m_terrain->LoadMapAndVMap(gx, gy); } } @@ -1844,17 +1609,7 @@ bool Map::UnloadGrid(NGridType& ngrid, bool unloadAll) int gx = (MAX_NUMBER_OF_GRIDS - 1) - x; int gy = (MAX_NUMBER_OF_GRIDS - 1) - y; - // delete grid map, but don't delete if it is from parent map (and thus only reference) - if (GridMaps[gx][gy]) - { - Map* terrainRoot = m_parentMap->GetRootParentTerrainMap(); - if (!--terrainRoot->GridMapReference[gx][gy]) - { - m_parentMap->GetRootParentTerrainMap()->UnloadMap(gx, gy); - VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(terrainRoot->GetId(), gx, gy); - MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(terrainRoot->GetId(), gx, gy); - } - } + m_terrain->UnloadMap(gx, gy); TC_LOG_DEBUG("maps", "Unloading grid[%u, %u] for map %u finished", x, y, GetId()); return true; @@ -1913,1124 +1668,77 @@ void Map::UnloadAll() _corpseBones.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; - _minHeightPlanes = nullptr; - // Liquid data - _liquidGlobalEntry = 0; - _liquidGlobalFlags = map_liquidHeaderTypeFlags::NoWater; - _liquidOffX = 0; - _liquidOffY = 0; - _liquidWidth = 0; - _liquidHeight = 0; - _liquidLevel = INVALID_HEIGHT; - _liquidEntry = nullptr; - _liquidFlags = nullptr; - _liquidMap = nullptr; - _holes = nullptr; -} - -GridMap::~GridMap() -{ - unloadData(); -} - -GridMap::LoadResult GridMap::loadData(char const* 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 LoadResult::FileDoesNotExist; - - if (fread(&header, sizeof(header), 1, in) != 1) - { - fclose(in); - return LoadResult::InvalidFile; - } - - if (header.mapMagic == MapMagic && header.versionMagic == MapVersionMagic) - { - // load up area data - if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize)) - { - TC_LOG_ERROR("maps", "Error loading map area data\n"); - fclose(in); - return LoadResult::InvalidFile; - } - // load up height data - if (header.heightMapOffset && !loadHeightData(in, header.heightMapOffset, header.heightMapSize)) - { - TC_LOG_ERROR("maps", "Error loading map height data\n"); - fclose(in); - return LoadResult::InvalidFile; - } - // load up liquid data - if (header.liquidMapOffset && !loadLiquidData(in, header.liquidMapOffset, header.liquidMapSize)) - { - TC_LOG_ERROR("maps", "Error loading map liquids data\n"); - fclose(in); - return LoadResult::InvalidFile; - } - // loadup holes data (if any. check header.holesOffset) - if (header.holesSize && !loadHolesData(in, header.holesOffset, header.holesSize)) - { - TC_LOG_ERROR("maps", "Error loading map holes data\n"); - fclose(in); - return LoadResult::InvalidFile; - } - fclose(in); - return LoadResult::Ok; - } - - TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s v%u), %.*s v%u 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, 4, header.mapMagic.data(), header.versionMagic, 4, MapMagic.data(), MapVersionMagic); - fclose(in); - return LoadResult::InvalidFile; -} - -void GridMap::unloadData() -{ - delete[] _areaMap; - delete[] m_V9; - delete[] m_V8; - delete[] _minHeightPlanes; - delete[] _liquidEntry; - delete[] _liquidFlags; - delete[] _liquidMap; - delete[] _holes; - _areaMap = nullptr; - m_V9 = nullptr; - m_V8 = nullptr; - _minHeightPlanes = nullptr; - _liquidEntry = nullptr; - _liquidFlags = nullptr; - _liquidMap = nullptr; - _holes = 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.areaMagic != MapAreaMagic) - return false; - - _gridArea = header.gridArea; - if (!header.flags.HasFlag(map_areaHeaderFlags::NoArea)) - { - _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.heightMagic != MapHeightMagic) - return false; - - _gridHeight = header.gridHeight; - if (!header.flags.HasFlag(map_heightHeaderFlags::NoHeight)) - { - if (header.flags.HasFlag(map_heightHeaderFlags::HeightAsInt16)) - { - 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.HasFlag(map_heightHeaderFlags::HeightAsInt8)) - { - 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.HasFlag(map_heightHeaderFlags::HasFlightBounds)) - { - std::array<int16, 9> maxHeights; - std::array<int16, 9> minHeights; - if (fread(maxHeights.data(), sizeof(int16), maxHeights.size(), in) != maxHeights.size() || - fread(minHeights.data(), sizeof(int16), minHeights.size(), in) != minHeights.size()) - return false; - - static uint32 constexpr indices[8][3] = - { - { 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 constexpr boundGridCoords[9][2] = - { - { 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 } - }; - - _minHeightPlanes = new G3D::Plane[8]; - for (uint32 quarterIndex = 0; quarterIndex < 8; ++quarterIndex) - _minHeightPlanes[quarterIndex] = G3D::Plane( - G3D::Vector3(boundGridCoords[indices[quarterIndex][0]][0], boundGridCoords[indices[quarterIndex][0]][1], minHeights[indices[quarterIndex][0]]), - G3D::Vector3(boundGridCoords[indices[quarterIndex][1]][0], boundGridCoords[indices[quarterIndex][1]][1], minHeights[indices[quarterIndex][1]]), - G3D::Vector3(boundGridCoords[indices[quarterIndex][2]][0], boundGridCoords[indices[quarterIndex][2]][1], minHeights[indices[quarterIndex][2]]) - ); - } - - 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.liquidMagic != MapLiquidMagic) - return false; - - _liquidGlobalEntry = header.liquidType; - _liquidGlobalFlags = header.liquidFlags; - _liquidOffX = header.offsetX; - _liquidOffY = header.offsetY; - _liquidWidth = header.width; - _liquidHeight = header.height; - _liquidLevel = header.liquidLevel; - - if (!header.flags.HasFlag(map_liquidHeaderFlags::NoType)) - { - _liquidEntry = new uint16[16*16]; - if (fread(_liquidEntry, sizeof(uint16), 16*16, in) != 16*16) - return false; - - _liquidFlags = new map_liquidHeaderTypeFlags[16*16]; - if (fread(_liquidFlags, sizeof(map_liquidHeaderTypeFlags), 16*16, in) != 16*16) - return false; - } - if (!header.flags.HasFlag(map_liquidHeaderFlags::NoHeight)) - { - _liquidMap = new float[uint32(_liquidWidth) * uint32(_liquidHeight)]; - if (fread(_liquidMap, sizeof(float), _liquidWidth*_liquidHeight, in) != (uint32(_liquidWidth) * uint32(_liquidHeight))) - return false; - } - return true; -} - -bool GridMap::loadHolesData(FILE* in, uint32 offset, uint32 /*size*/) -{ - if (fseek(in, offset, SEEK_SET) != 0) - return false; - - _holes = new uint8[16 * 16 * 8]; - if (fread(_holes, sizeof(uint8), 16 * 16 * 8, in) != 16 * 16 * 8) - return false; - - return true; -} - -uint16 GridMap::getArea(float x, float y) const -{ - if (!_areaMap) - return _gridArea; - - x = 16 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - y = 16 * (CENTER_GRID_ID - 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 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - y = MAP_RESOLUTION * (CENTER_GRID_ID - 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); - - if (isHole(x_int, y_int)) - return INVALID_HEIGHT; - - // 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 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - y = MAP_RESOLUTION * (CENTER_GRID_ID - 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); - - if (isHole(x_int, y_int)) - return INVALID_HEIGHT; - - 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 * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - y = MAP_RESOLUTION * (CENTER_GRID_ID - 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); - - if (isHole(x_int, y_int)) - return INVALID_HEIGHT; - - 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; -} - -bool GridMap::isHole(int row, int col) const -{ - if (!_holes) - return false; - - int cellRow = row / 8; // 8 squares per cell - int cellCol = col / 8; - int holeRow = row % 8; - int holeCol = col % 8; - - return (_holes[cellRow * 16 * 8 + cellCol * 8 + holeRow] & (1 << holeCol)) != 0; -} - -float GridMap::getMinHeight(float x, float y) const -{ - if (!_minHeightPlanes) - return -500.0f; - - GridCoord gridCoord = Trinity::ComputeGridCoordSimple(x, y); - - int32 doubleGridX = int32(std::floor(-(x - MAP_HALFSIZE) / CENTER_GRID_OFFSET)); - int32 doubleGridY = int32(std::floor(-(y - MAP_HALFSIZE) / CENTER_GRID_OFFSET)); - - float gx = x - (int32(gridCoord.x_coord) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; - float gy = y - (int32(gridCoord.y_coord) - CENTER_GRID_ID + 1) * SIZE_OF_GRIDS; - - uint32 quarterIndex = 0; - if (doubleGridY & 1) - { - if (doubleGridX & 1) - quarterIndex = 4 + (gx <= gy); - else - quarterIndex = 2 + ((-SIZE_OF_GRIDS - gx) > gy); - } - else if (doubleGridX & 1) - quarterIndex = 6 + ((-SIZE_OF_GRIDS - gx) <= gy); - else - quarterIndex = gx > gy; - - G3D::Ray ray = G3D::Ray::fromOriginAndDirection(G3D::Vector3(gx, gy, 0.0f), G3D::Vector3::unitZ()); - return ray.intersection(_minHeightPlanes[quarterIndex]).z; -} - -float GridMap::getLiquidLevel(float x, float y) const -{ - if (!_liquidMap) - return _liquidLevel; - - x = MAP_RESOLUTION * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - y = MAP_RESOLUTION * (CENTER_GRID_ID - 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]; -} - -// Get water state on map -inline ZLiquidStatus GridMap::GetLiquidStatus(float x, float y, float z, Optional<map_liquidHeaderTypeFlags> ReqLiquidType, LiquidData* data, float collisionHeight) +void Map::GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, + map_liquidHeaderTypeFlags reqLiquidType, float collisionHeight) { - // Check water type (if no water return) - if (_liquidGlobalFlags == map_liquidHeaderTypeFlags::NoWater && !_liquidFlags) - return LIQUID_MAP_NO_WATER; - - // Get cell - float cx = MAP_RESOLUTION * (CENTER_GRID_ID - x/SIZE_OF_GRIDS); - float cy = MAP_RESOLUTION * (CENTER_GRID_ID - 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); - map_liquidHeaderTypeFlags type = _liquidFlags ? _liquidFlags[idx] : _liquidGlobalFlags; - uint32 entry = _liquidEntry ? _liquidEntry[idx] : _liquidGlobalEntry; - if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(entry)) - { - type &= map_liquidHeaderTypeFlags::DarkWater; - uint32 liqTypeIdx = liquidEntry->SoundBank; - if (entry < 21) - { - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) - { - uint32 overrideLiquid = area->LiquidTypeID[liquidEntry->SoundBank]; - if (!overrideLiquid && area->ParentAreaID) - { - area = sAreaTableStore.LookupEntry(area->ParentAreaID); - if (area) - overrideLiquid = area->LiquidTypeID[liquidEntry->SoundBank]; - } - - if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) - { - entry = overrideLiquid; - liqTypeIdx = liq->SoundBank; - } - } - } - - type |= map_liquidHeaderTypeFlags(1 << liqTypeIdx); - } - - if (type == map_liquidHeaderTypeFlags::NoWater) - return LIQUID_MAP_NO_WATER; - - // Check req liquid type mask - if (ReqLiquidType && (*ReqLiquidType & type) == map_liquidHeaderTypeFlags::NoWater) - 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) - 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 > collisionHeight) // 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; + m_terrain->GetFullTerrainStatusForPosition(phaseShift, x, y, z, data, reqLiquidType, collisionHeight, &_dynamicTree); } -GridMap* Map::GetGrid(uint32 mapId, float x, float y) +ZLiquidStatus Map::GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, map_liquidHeaderTypeFlags ReqLiquidType, LiquidData* data, + float collisionHeight) { - // half opt method - int gx = (int)(CENTER_GRID_ID - x / SIZE_OF_GRIDS); //grid x - int gy = (int)(CENTER_GRID_ID - y / SIZE_OF_GRIDS); //grid y - - // ensure GridMap is loaded - EnsureGridCreated(GridCoord((MAX_NUMBER_OF_GRIDS - 1) - gx, (MAX_NUMBER_OF_GRIDS - 1) - gy)); - - GridMap* grid = GridMaps[gx][gy].get(); - auto childMapItr = std::find_if(m_childTerrainMaps->begin(), m_childTerrainMaps->end(), [mapId](Map* childTerrainMap) { return childTerrainMap->GetId() == mapId; }); - if (childMapItr != m_childTerrainMaps->end() && (*childMapItr)->GridMaps[gx][gy]) - grid = (*childMapItr)->GridMaps[gx][gy].get(); - - return grid; + return m_terrain->GetLiquidStatus(phaseShift, x, y, z, ReqLiquidType, data, collisionHeight); } -bool Map::HasChildMapGridFile(uint32 mapId, int32 gx, int32 gy) const +bool Map::GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) { - auto childMapItr = std::find_if(m_childTerrainMaps->begin(), m_childTerrainMaps->end(), [mapId](Map* childTerrainMap) { return childTerrainMap->GetId() == mapId; }); - return childMapItr != m_childTerrainMaps->end() && (*childMapItr)->i_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy]; + return m_terrain->GetAreaInfo(phaseShift, x, y, z, mogpflags, adtId, rootId, groupId, &_dynamicTree); } -float Map::GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground /*= nullptr*/, bool /*swim = false*/, float collisionHeight /*= DEFAULT_COLLISION_HEIGHT*/) +uint32 Map::GetAreaId(PhaseShift const& phaseShift, float x, float y, float z) { - if (GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - { - // we need ground level (including grid height version) for proper return water level in point - float ground_z = GetHeight(phaseShift, x, y, z + Z_OFFSET_FIND_HEIGHT, true, 50.0f); - if (ground) - *ground = ground_z; - - LiquidData liquid_status; - - ZLiquidStatus res = GetLiquidStatus(phaseShift, x, y, ground_z, map_liquidHeaderTypeFlags::AllLiquids, &liquid_status, collisionHeight); - switch (res) - { - case LIQUID_MAP_ABOVE_WATER: - return std::max<float>(liquid_status.level, ground_z); - case LIQUID_MAP_NO_WATER: - return ground_z; - default: - return liquid_status.level; - } - } - - return VMAP_INVALID_HEIGHT_VALUE; + return m_terrain->GetAreaId(phaseShift, x, y, z, &_dynamicTree); } -float Map::GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) +uint32 Map::GetZoneId(PhaseShift const& phaseShift, float x, float y, float z) { - // find raw .map surface under Z coordinates - float mapHeight = VMAP_INVALID_HEIGHT_VALUE; - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); - float gridHeight = GetGridHeight(phaseShift, x, y); - if (G3D::fuzzyGe(z, gridHeight - GROUND_HEIGHT_TOLERANCE)) - mapHeight = gridHeight; - - float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; - if (checkVMap) - { - VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); - if (vmgr->isHeightCalcEnabled()) - vmapHeight = vmgr->getHeight(terrainMapId, x, y, z, maxSearchDist); - } - - // 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 - - // vmap height above map height - // or if the distance of the vmap height is less the land height distance - if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z)) - return vmapHeight; - - return mapHeight; // better use .map surface height - } - - return vmapHeight; // we have only vmapHeight (if have) - } - - return mapHeight; // explicitly use map data + return m_terrain->GetZoneId(phaseShift, x, y, z, &_dynamicTree); } -float Map::GetGridHeight(PhaseShift const& phaseShift, float x, float y) +void Map::GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z) { - if (GridMap* gmap = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - return gmap->getHeight(x, y); - - return VMAP_INVALID_HEIGHT_VALUE; + return m_terrain->GetZoneAndAreaId(phaseShift, zoneid, areaid, x, y, z, &_dynamicTree); } float Map::GetMinHeight(PhaseShift const& phaseShift, float x, float y) { - if (GridMap const* grid = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - return grid->getMinHeight(x, y); - - return -500.0f; + return m_terrain->GetMinHeight(phaseShift, x, y); } -static inline bool IsInWMOInterior(uint32 mogpFlags) -{ - return (mogpFlags & 0x2000) != 0; -} - -bool Map::GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) -{ - float vmap_z = z; - float dynamic_z = z; - float check_z = z; - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); - VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); - uint32 vflags; - int32 vadtId; - int32 vrootId; - int32 vgroupId; - uint32 dflags; - int32 dadtId; - int32 drootId; - int32 dgroupId; - - bool hasVmapAreaInfo = vmgr->getAreaInfo(terrainMapId, x, y, vmap_z, vflags, vadtId, vrootId, vgroupId); - bool hasDynamicAreaInfo = _dynamicTree.getAreaInfo(x, y, dynamic_z, phaseShift, dflags, dadtId, drootId, dgroupId); - auto useVmap = [&]() { check_z = vmap_z; mogpflags = vflags; adtId = vadtId; rootId = vrootId; groupId = vgroupId; }; - auto useDyn = [&]() { check_z = dynamic_z; mogpflags = dflags; adtId = dadtId; rootId = drootId; groupId = dgroupId; }; - - if (hasVmapAreaInfo) - { - if (hasDynamicAreaInfo && dynamic_z > vmap_z) - useDyn(); - else - useVmap(); - } - else if (hasDynamicAreaInfo) - { - useDyn(); - } - - if (hasVmapAreaInfo || hasDynamicAreaInfo) - { - // check if there's terrain between player height and object height - if (GridMap* gmap = GetGrid(terrainMapId, 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 > check_z) - return false; - } - return true; - } - return false; -} - -uint32 Map::GetAreaId(PhaseShift const& phaseShift, float x, float y, float z) +float Map::GetGridHeight(PhaseShift const& phaseShift, float x, float y) { - uint32 mogpFlags; - int32 adtId, rootId, groupId; - float vmapZ = z; - bool hasVmapArea = GetAreaInfo(phaseShift, x, y, vmapZ, mogpFlags, adtId, rootId, groupId); - - uint32 gridAreaId = 0; - float gridMapHeight = INVALID_HEIGHT; - if (GridMap* gmap = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - { - gridAreaId = gmap->getArea(x, y); - gridMapHeight = gmap->getHeight(x, y); - } - - uint32 areaId = 0; - - // floor is the height we are closer to (but only if above) - if (hasVmapArea && G3D::fuzzyGe(z, vmapZ - GROUND_HEIGHT_TOLERANCE) && (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapZ > gridMapHeight)) - { - // wmo found - if (WMOAreaTableEntry const* wmoEntry = sDB2Manager.GetWMOAreaTable(rootId, adtId, groupId)) - areaId = wmoEntry->AreaTableID; - - if (!areaId) - areaId = gridAreaId; - } - else - areaId = gridAreaId; - - if (!areaId) - areaId = i_mapEntry->AreaTableID; - - return areaId; + return m_terrain->GetGridHeight(phaseShift, x, y); } -uint32 Map::GetZoneId(PhaseShift const& phaseShift, float x, float y, float z) +float Map::GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap, float maxSearchDist) { - uint32 areaId = GetAreaId(phaseShift, x, y, z); - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) - if (area->ParentAreaID) - return area->ParentAreaID; - - return areaId; + return m_terrain->GetStaticHeight(phaseShift, x, y, z, checkVMap, maxSearchDist); } -void Map::GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z) +float Map::GetWaterLevel(PhaseShift const& phaseShift, float x, float y) { - areaid = zoneid = GetAreaId(phaseShift, x, y, z); - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid)) - if (area->ParentAreaID) - zoneid = area->ParentAreaID; + return m_terrain->GetWaterLevel(phaseShift, x, y); } -ZLiquidStatus Map::GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, map_liquidHeaderTypeFlags ReqLiquidType, LiquidData* data, float collisionHeight) +bool Map::IsInWater(PhaseShift const& phaseShift, float x, float y, float z, LiquidData* data) { - 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; - uint32 mogpFlags = 0; - bool useGridLiquid = true; - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); - if (vmgr->GetLiquidLevel(terrainMapId, x, y, z, AsUnderlyingType(ReqLiquidType), liquid_level, ground_level, liquid_type, mogpFlags)) - { - useGridLiquid = !IsInWMOInterior(mogpFlags); - TC_LOG_DEBUG("maps", "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 && G3D::fuzzyGe(z, ground_level - GROUND_HEIGHT_TOLERANCE)) - { - // 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->SoundBank; - - if (liquid_type && liquid_type < 21) - { - if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(phaseShift, x, y, z))) - { - uint32 overrideLiquid = area->LiquidTypeID[liquidFlagType]; - if (!overrideLiquid && area->ParentAreaID) - { - area = sAreaTableStore.LookupEntry(area->ParentAreaID); - if (area) - overrideLiquid = area->LiquidTypeID[liquidFlagType]; - } - - if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) - { - liquid_type = overrideLiquid; - liquidFlagType = liq->SoundBank; - } - } - } - - data->level = liquid_level; - data->depth_level = ground_level; - - data->entry = liquid_type; - data->type_flags = map_liquidHeaderTypeFlags(1 << liquidFlagType); - } - - float delta = liquid_level - z; - - // Get position delta - if (delta > collisionHeight) // 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 (useGridLiquid) - { - if (GridMap* gmap = GetGrid(terrainMapId, x, y)) - { - LiquidData map_data; - ZLiquidStatus map_result = gmap->GetLiquidStatus(x, y, z, ReqLiquidType, &map_data, collisionHeight); - // 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; + return m_terrain->IsInWater(phaseShift, x, y, z, data); } -void Map::GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, map_liquidHeaderTypeFlags reqLiquidType, float collisionHeight) +bool Map::IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z) { - VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); - VMAP::AreaAndLiquidData vmapData; - VMAP::AreaAndLiquidData dynData; - VMAP::AreaAndLiquidData* wmoData = nullptr; - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); - GridMap* gmap = GetGrid(terrainMapId, x, y); - vmgr->getAreaAndLiquidData(terrainMapId, x, y, z, AsUnderlyingType(reqLiquidType), vmapData); - _dynamicTree.getAreaAndLiquidData(x, y, z, phaseShift, AsUnderlyingType(reqLiquidType), dynData); - - uint32 gridAreaId = 0; - float gridMapHeight = INVALID_HEIGHT; - if (gmap) - { - gridAreaId = gmap->getArea(x, y); - gridMapHeight = gmap->getHeight(x, y); - } - - bool useGridLiquid = true; - - // floor is the height we are closer to (but only if above) - data.floorZ = VMAP_INVALID_HEIGHT; - if (gridMapHeight > INVALID_HEIGHT && G3D::fuzzyGe(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE)) - data.floorZ = gridMapHeight; - if (vmapData.floorZ > VMAP_INVALID_HEIGHT && - G3D::fuzzyGe(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) && - (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapData.floorZ > gridMapHeight)) - { - data.floorZ = vmapData.floorZ; - wmoData = &vmapData; - } - // NOTE: Objects will not detect a case when a wmo providing area/liquid despawns from under them - // but this is fine as these kind of objects are not meant to be spawned and despawned a lot - // example: Lich King platform - if (dynData.floorZ > VMAP_INVALID_HEIGHT && - G3D::fuzzyGe(z, dynData.floorZ - GROUND_HEIGHT_TOLERANCE) && - (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > gridMapHeight) && - (G3D::fuzzyLt(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > vmapData.floorZ)) - { - data.floorZ = dynData.floorZ; - wmoData = &dynData; - } - - if (wmoData) - { - if (wmoData->areaInfo) - { - data.areaInfo.emplace(wmoData->areaInfo->adtId, wmoData->areaInfo->rootId, wmoData->areaInfo->groupId, wmoData->areaInfo->mogpFlags); - // wmo found - WMOAreaTableEntry const* wmoEntry = sDB2Manager.GetWMOAreaTable(wmoData->areaInfo->rootId, wmoData->areaInfo->adtId, wmoData->areaInfo->groupId); - data.outdoors = (wmoData->areaInfo->mogpFlags & 0x8) != 0; - if (wmoEntry) - { - data.areaId = wmoEntry->AreaTableID; - if (wmoEntry->Flags & 4) - data.outdoors = true; - else if (wmoEntry->Flags & 2) - data.outdoors = false; - } - - if (!data.areaId) - data.areaId = gridAreaId; - - useGridLiquid = !IsInWMOInterior(wmoData->areaInfo->mogpFlags); - } - } - else - { - data.outdoors = true; - data.areaId = gridAreaId; - if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId)) - data.outdoors = (areaEntry->Flags[0] & (AREA_FLAG_INSIDE | AREA_FLAG_OUTSIDE)) != AREA_FLAG_INSIDE; - } - - if (!data.areaId) - data.areaId = i_mapEntry->AreaTableID; - - AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId); - - // liquid processing - data.liquidStatus = LIQUID_MAP_NO_WATER; - if (wmoData && wmoData->liquidInfo && wmoData->liquidInfo->level > wmoData->floorZ) - { - uint32 liquidType = wmoData->liquidInfo->type; - if (GetId() == 530 && liquidType == 2) // gotta love blizzard hacks - liquidType = 15; - - uint32 liquidFlagType = 0; - if (LiquidTypeEntry const* liquidData = sLiquidTypeStore.LookupEntry(liquidType)) - liquidFlagType = liquidData->SoundBank; - - if (liquidType && liquidType < 21 && areaEntry) - { - uint32 overrideLiquid = areaEntry->LiquidTypeID[liquidFlagType]; - if (!overrideLiquid && areaEntry->ParentAreaID) - { - AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(areaEntry->ParentAreaID); - if (zoneEntry) - overrideLiquid = zoneEntry->LiquidTypeID[liquidFlagType]; - } - - if (LiquidTypeEntry const* overrideData = sLiquidTypeStore.LookupEntry(overrideLiquid)) - { - liquidType = overrideLiquid; - liquidFlagType = overrideData->SoundBank; - } - } - - data.liquidInfo.emplace(); - data.liquidInfo->level = wmoData->liquidInfo->level; - data.liquidInfo->depth_level = wmoData->floorZ; - data.liquidInfo->entry = liquidType; - data.liquidInfo->type_flags = map_liquidHeaderTypeFlags(1 << liquidFlagType); - - float delta = wmoData->liquidInfo->level - z; - if (delta > collisionHeight) - data.liquidStatus = LIQUID_MAP_UNDER_WATER; - else if (delta > 0.0f) - data.liquidStatus = LIQUID_MAP_IN_WATER; - else if (delta > -0.1f) - data.liquidStatus = LIQUID_MAP_WATER_WALK; - else - data.liquidStatus = LIQUID_MAP_ABOVE_WATER; - } - // look up liquid data from grid map - if (gmap && useGridLiquid) - { - LiquidData gridMapLiquid; - ZLiquidStatus gridMapStatus = gmap->GetLiquidStatus(x, y, z, reqLiquidType, &gridMapLiquid, collisionHeight); - if (gridMapStatus != LIQUID_MAP_NO_WATER && (!wmoData || gridMapLiquid.level > wmoData->floorZ)) - { - if (GetId() == 530 && gridMapLiquid.entry == 2) - gridMapLiquid.entry = 15; - data.liquidInfo = gridMapLiquid; - data.liquidStatus = gridMapStatus; - } - } + return m_terrain->IsUnderWater(phaseShift, x, y, z); } -float Map::GetWaterLevel(PhaseShift const& phaseShift, float x, float y) +float Map::GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground, bool swim, float collisionHeight) { - if (GridMap* gmap = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) - return gmap->getLiquidLevel(x, y); - else - return 0; + return m_terrain->GetWaterOrGroundLevel(phaseShift, x, y, z, ground, swim, collisionHeight, &_dynamicTree); } bool Map::isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const { if ((checks & LINEOFSIGHT_CHECK_VMAP) - && !VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(PhasingHandler::GetTerrainMapId(phaseShift, this, x1, y1), x1, y1, z1, x2, y2, z2, ignoreFlags)) + && !VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(PhasingHandler::GetTerrainMapId(phaseShift, m_terrain.get(), x1, y1), x1, y1, z1, x2, y2, z2, ignoreFlags)) return false; if (sWorld->getBoolConfig(CONFIG_CHECK_GOBJECT_LOS) && (checks & LINEOFSIGHT_CHECK_GOBJECT) && !_dynamicTree.isInLineOfSight({ x1, y1, z1 }, { x2, y2, z2 }, phaseShift)) @@ -3052,21 +1760,9 @@ bool Map::getObjectHitPos(PhaseShift const& phaseShift, float x1, float y1, floa return result; } -bool Map::IsInWater(PhaseShift const& phaseShift, float x, float y, float pZ, LiquidData* data) -{ - LiquidData liquid_status; - LiquidData* liquid_ptr = data ? data : &liquid_status; - return (GetLiquidStatus(phaseShift, x, y, pZ, map_liquidHeaderTypeFlags::AllLiquids, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) != 0; -} - -bool Map::IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z) -{ - return (GetLiquidStatus(phaseShift, x, y, z, map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean) & LIQUID_MAP_UNDER_WATER) != 0; -} - char const* Map::GetMapName() const { - return i_mapEntry ? i_mapEntry->MapName[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0"; + return i_mapEntry->MapName[sWorld->GetDefaultDbcLocale()]; } void Map::SendInitSelf(Player* player) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 89f64d947e0..471d1b64d5a 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -30,7 +30,6 @@ #include "MapRefManager.h" #include "MPSCQueue.h" #include "ObjectGuid.h" -#include "Optional.h" #include "PersonalPhaseTracker.h" #include "SharedDefines.h" #include "SpawnData.h" @@ -60,6 +59,7 @@ class PhaseShift; class Player; class SpawnedPoolData; class TempSummon; +class TerrainInfo; class Unit; class Weather; class WorldObject; @@ -77,7 +77,6 @@ enum WeatherState : uint32; enum class ItemContext : uint8; namespace Trinity { struct ObjectUpdater; } -namespace G3D { class Plane; } namespace VMAP { enum class ModelIgnoreFlags : uint32; } struct ScriptAction @@ -88,119 +87,6 @@ struct ScriptAction ScriptInfo const* script; ///> pointer to static script data }; -enum ZLiquidStatus : uint32 -{ - LIQUID_MAP_NO_WATER = 0x00000000, - LIQUID_MAP_ABOVE_WATER = 0x00000001, - LIQUID_MAP_WATER_WALK = 0x00000002, - LIQUID_MAP_IN_WATER = 0x00000004, - LIQUID_MAP_UNDER_WATER = 0x00000008 -}; - -#define MAP_LIQUID_STATUS_SWIMMING (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER) -#define MAP_LIQUID_STATUS_IN_CONTACT (MAP_LIQUID_STATUS_SWIMMING | LIQUID_MAP_WATER_WALK) - -struct LiquidData -{ - EnumFlag<map_liquidHeaderTypeFlags> type_flags = map_liquidHeaderTypeFlags::NoWater; - uint32 entry; - float level; - float depth_level; -}; - -struct PositionFullTerrainStatus -{ - struct AreaInfo - { - AreaInfo(int32 _adtId, int32 _rootId, int32 _groupId, uint32 _flags) : adtId(_adtId), rootId(_rootId), groupId(_groupId), mogpFlags(_flags) { } - int32 const adtId; - int32 const rootId; - int32 const groupId; - uint32 const mogpFlags; - }; - - PositionFullTerrainStatus() : areaId(0), floorZ(0.0f), outdoors(true), liquidStatus(LIQUID_MAP_NO_WATER) { } - uint32 areaId; - float floorZ; - bool outdoors; - ZLiquidStatus liquidStatus; - Optional<AreaInfo> areaInfo; - Optional<LiquidData> liquidInfo; -}; - -class TC_GAME_API GridMap -{ - uint32 _flags; - union{ - float* m_V9; - uint16* m_uint16_V9; - uint8* m_uint8_V9; - }; - union{ - float* m_V8; - uint16* m_uint16_V8; - uint8* m_uint8_V8; - }; - G3D::Plane* _minHeightPlanes; - // Height level data - float _gridHeight; - float _gridIntHeightMultiplier; - - // Area data - uint16* _areaMap; - - // Liquid data - float _liquidLevel; - uint16* _liquidEntry; - map_liquidHeaderTypeFlags* _liquidFlags; - float* _liquidMap; - uint16 _gridArea; - uint16 _liquidGlobalEntry; - map_liquidHeaderTypeFlags _liquidGlobalFlags; - uint8 _liquidOffX; - uint8 _liquidOffY; - uint8 _liquidWidth; - uint8 _liquidHeight; - - uint8* _holes; - - bool loadAreaData(FILE* in, uint32 offset, uint32 size); - bool loadHeightData(FILE* in, uint32 offset, uint32 size); - bool loadLiquidData(FILE* in, uint32 offset, uint32 size); - bool loadHolesData(FILE* in, uint32 offset, uint32 size); - bool isHole(int row, int col) const; - - // Get height functions and pointers - typedef float (GridMap::*GetHeightPtr) (float x, float y) const; - GetHeightPtr _gridGetHeight; - float getHeightFromFloat(float x, float y) const; - float getHeightFromUint16(float x, float y) const; - float getHeightFromUint8(float x, float y) const; - float getHeightFromFlat(float x, float y) const; - -public: - GridMap(); - ~GridMap(); - - enum class LoadResult - { - Ok, - FileDoesNotExist, - InvalidFile - }; - - LoadResult loadData(char const* filename); - void unloadData(); - - uint16 getArea(float x, float y) const; - inline float getHeight(float x, float y) const {return (this->*_gridGetHeight)(x, y);} - float getMinHeight(float x, float y) const; - float getLiquidLevel(float x, float y) const; - ZLiquidStatus GetLiquidStatus(float x, float y, float z, Optional<map_liquidHeaderTypeFlags> ReqLiquidType, LiquidData* data = nullptr, float collisionHeight = 2.03128f); // DEFAULT_COLLISION_HEIGHT in Object.h -}; - -#pragma pack(push, 1) - struct ZoneDynamicInfo { ZoneDynamicInfo(); @@ -220,12 +106,6 @@ struct ZoneDynamicInfo std::vector<LightOverride> LightOverrides; }; -#pragma pack(pop) - -#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface -#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE -#define MAX_FALL_DISTANCE 250000.0f // "unlimited fall" to find VMap ground if it is available, just larger than MAX_HEIGHT - INVALID_HEIGHT -#define DEFAULT_HEIGHT_SEARCH 50.0f // default search distance to find height at nearby locations #define MIN_UNLOAD_DELAY 1 // immediate unload #define MAP_INVALID_ZONE 0xFFFFFFFF @@ -333,20 +213,11 @@ class TC_GAME_API Map : public GridRefManager<NGridType> time_t GetGridExpiry() const { return i_gridExpiry; } - bool HasChildMapGridFile(uint32 mapId, int32 gx, int32 gy) const; - static bool ExistMap(uint32 mapid, int gx, int gy, bool log = true); - static bool ExistVMap(uint32 mapid, int gx, int gy); - static void InitStateMachine(); static void DeleteStateMachine(); - void DiscoverGridMapFiles(); - - Map* GetRootParentTerrainMap(); - void AddChildTerrainMap(Map* map) { m_childTerrainMaps->push_back(map); map->m_parentTerrainMap = this; } - void UnlinkAllChildTerrainMaps() { m_childTerrainMaps->clear(); } + TerrainInfo* GetTerrain() const { return m_terrain.get(); } - void GetFullTerrainStatusForPosition(float x, float y, float z, PositionFullTerrainStatus& data, map_liquidHeaderTypeFlags reqLiquidType = map_liquidHeaderTypeFlags::AllLiquids, float collisionHeight = 2.03128f) const; // DEFAULT_COLLISION_HEIGHT in Object.h void GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, map_liquidHeaderTypeFlags reqLiquidType = map_liquidHeaderTypeFlags::AllLiquids, float collisionHeight = 2.03128f); // DEFAULT_COLLISION_HEIGHT in Object.h ZLiquidStatus GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, map_liquidHeaderTypeFlags ReqLiquidType, LiquidData* data = nullptr, float collisionHeight = 2.03128f); // DEFAULT_COLLISION_HEIGHT in Object.h @@ -358,10 +229,19 @@ class TC_GAME_API Map : public GridRefManager<NGridType> void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z); void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, Position const& pos) { GetZoneAndAreaId(phaseShift, zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + float GetMinHeight(PhaseShift const& phaseShift, float x, float y); + float GetGridHeight(PhaseShift const& phaseShift, float x, float y); + float GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH); + float GetStaticHeight(PhaseShift const& phaseShift, Position const& pos, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetStaticHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), checkVMap, maxSearchDist); } + float GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return std::max<float>(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist)); } + float GetHeight(PhaseShift const& phaseShift, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); } + float GetWaterLevel(PhaseShift const& phaseShift, float x, float y); bool IsInWater(PhaseShift const& phaseShift, float x, float y, float z, LiquidData* data = nullptr); bool IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z); + float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = 2.03128f); // DEFAULT_COLLISION_HEIGHT in Object.h + void MoveAllCreaturesInMoveList(); void MoveAllGameObjectsInMoveList(); void MoveAllDynamicObjectsInMoveList(); @@ -531,13 +411,6 @@ class TC_GAME_API Map : public GridRefManager<NGridType> BattlegroundMap* ToBattlegroundMap() { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap*>(this); else return nullptr; } BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return nullptr; } - float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = 2.03128f); // DEFAULT_COLLISION_HEIGHT in Object.h - float GetMinHeight(PhaseShift const& phaseShift, float x, float y); - float GetGridHeight(PhaseShift const& phaseShift, float x, float y); - float GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH); - float GetStaticHeight(PhaseShift const& phaseShift, Position const& pos, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetStaticHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), checkVMap, maxSearchDist); } - float GetHeight(PhaseShift const& phaseShift, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return std::max<float>(GetStaticHeight(phaseShift, x, y, z, vmap, maxSearchDist), GetGameObjectFloor(phaseShift, x, y, z, maxSearchDist)); } - float GetHeight(PhaseShift const& phaseShift, Position const& pos, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), vmap, maxSearchDist); } bool isInLineOfSight(PhaseShift const& phaseShift, float x1, float y1, float z1, float x2, float y2, float z2, LineOfSightChecks checks, VMAP::ModelIgnoreFlags ignoreFlags) const; void Balance() { _dynamicTree.balance(); } void RemoveGameObjectModel(GameObjectModel const& model) { _dynamicTree.remove(model); } @@ -628,14 +501,6 @@ class TC_GAME_API Map : public GridRefManager<NGridType> virtual std::string GetDebugInfo() const; private: - void LoadMapAndVMap(int gx, int gy); - void LoadVMap(int gx, int gy); - void LoadMap(int gx, int gy); - static void LoadMapImpl(Map* map, int gx, int gy); - void UnloadMap(int gx, int gy); - static void UnloadMapImpl(Map* map, int gx, int gy); - void LoadMMap(int gx, int gy); - GridMap* GetGrid(uint32 mapId, float x, float y); void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; } @@ -733,16 +598,9 @@ class TC_GAME_API Map : public GridRefManager<NGridType> time_t i_gridExpiry; - //used for fast base_map (e.g. MapInstanced class object) search for - //InstanceMaps and BattlegroundMaps... - Map* m_parentMap; // points to MapInstanced* or self (always same map id) - Map* m_parentTerrainMap; // points to m_parentMap of MapEntry::ParentMapID - std::vector<Map*>* m_childTerrainMaps; // contains m_parentMap of maps that have MapEntry::ParentMapID == GetId() + std::shared_ptr<TerrainInfo> m_terrain; NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; - std::shared_ptr<GridMap> GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; - uint16 GridMapReference[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; - std::bitset<MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS> i_gridFileExists; // cache what grids are available for this map (not including parent/child maps) std::bitset<TOTAL_NUMBER_OF_CELLS_PER_MAP*TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells; //these functions used to process player/mob aggro reactions and diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index ae040530b3c..a962cbecac8 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -52,11 +52,6 @@ void MapManager::Initialize() m_updater.activate(num_threads); } -void MapManager::InitializeParentMapData(std::unordered_map<uint32, std::vector<uint32>> const& mapData) -{ - _parentMapData = mapData; -} - void MapManager::InitializeVisibilityDistanceInfo() { for (auto iter = i_maps.begin(); iter != i_maps.end(); ++iter) @@ -101,13 +96,9 @@ Map* MapManager::CreateBaseMap_i(MapEntry const* mapEntry) else map = new Map(mapEntry->ID, i_gridCleanUpDelay, 0, DIFFICULTY_NONE); - map->DiscoverGridMapFiles(); i_maps[mapEntry->ID] = map; - for (uint32 childMapId : _parentMapData[mapEntry->ID]) - map->AddChildTerrainMap(CreateBaseMap_i(sMapStore.AssertEntry(childMapId))); - if (!mapEntry->Instanceable()) { map->LoadRespawnTimes(); @@ -252,16 +243,6 @@ void MapManager::Update(uint32 diff) void MapManager::DoDelayedMovesAndRemoves() { } -bool MapManager::ExistMapAndVMap(uint32 mapid, float x, float y) -{ - GridCoord p = Trinity::ComputeGridCoord(x, y); - - int gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord; - int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord; - - return Map::ExistMap(mapid, gx, gy) && Map::ExistVMap(mapid, gx, gy); -} - bool MapManager::IsValidMAP(uint32 mapId) { return sMapStore.LookupEntry(mapId) != nullptr; diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 57bfd88ae3f..f2c3bbb1e86 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -37,30 +37,7 @@ class TC_GAME_API MapManager Map* CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId=0); Map* FindMap(uint32 mapId, uint32 instanceId) const; - uint32 GetAreaId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z) - { - Map* m = CreateBaseMap(mapid); - return m->GetAreaId(phaseShift, x, y, z); - } - uint32 GetAreaId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) { return GetAreaId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } - uint32 GetAreaId(PhaseShift const& phaseShift, WorldLocation const& loc) { return GetAreaId(phaseShift, loc.GetMapId(), loc); } - uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z) - { - Map* m = CreateBaseMap(mapid); - return m->GetZoneId(phaseShift, x, y, z); - } - uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) { return GetZoneId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } - uint32 GetZoneId(PhaseShift const& phaseShift, WorldLocation const& loc) { return GetZoneId(phaseShift, loc.GetMapId(), loc); } - void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) - { - Map* m = CreateBaseMap(mapid); - m->GetZoneAndAreaId(phaseShift, zoneid, areaid, x, y, z); - } - void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) { GetZoneAndAreaId(phaseShift, zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } - void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, WorldLocation const& loc) { GetZoneAndAreaId(phaseShift, zoneid, areaid, loc.GetMapId(), loc); } - void Initialize(); - void InitializeParentMapData(std::unordered_map<uint32, std::vector<uint32>> const& mapData); void Update(uint32); void SetGridCleanUpDelay(uint32 t) @@ -83,7 +60,6 @@ class TC_GAME_API MapManager //void LoadGrid(int mapid, int instId, float x, float y, WorldObject const* obj, bool no_unload = false); void UnloadAll(); - static bool ExistMapAndVMap(uint32 mapid, float x, float y); static bool IsValidMAP(uint32 mapId); static bool IsValidMapCoord(uint32 mapid, float x, float y) @@ -168,9 +144,6 @@ class TC_GAME_API MapManager // atomic op counter for active scripts amount std::atomic<std::size_t> _scheduledScripts; - - // parent map links - std::unordered_map<uint32, std::vector<uint32>> _parentMapData; }; template<typename Worker> diff --git a/src/server/game/Maps/TerrainMgr.cpp b/src/server/game/Maps/TerrainMgr.cpp new file mode 100644 index 00000000000..d1babe99ae1 --- /dev/null +++ b/src/server/game/Maps/TerrainMgr.cpp @@ -0,0 +1,828 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "TerrainMgr.h" +#include "DB2Stores.h" +#include "DisableMgr.h" +#include "DynamicTree.h" +#include "GridMap.h" +#include "Log.h" +#include "MMapFactory.h" +#include "PhasingHandler.h" +#include "Random.h" +#include "ScriptMgr.h" +#include "Util.h" +#include "VMapFactory.h" +#include "VMapManager2.h" +#include "World.h" +#include <G3D/g3dmath.h> + +TerrainInfo::TerrainInfo(uint32 mapId) : _mapId(mapId), _parentTerrain(nullptr), _cleanupTimer(randtime(CleanupInterval / 2, CleanupInterval)) +{ +} + +TerrainInfo::~TerrainInfo() = default; + +char const* TerrainInfo::GetMapName() const +{ + return sMapStore.AssertEntry(GetId())->MapName[sWorld->GetDefaultDbcLocale()]; +} + +void TerrainInfo::DiscoverGridMapFiles() +{ + std::string tileListName = Trinity::StringFormat("%smaps/%04u.tilelist", sWorld->GetDataPath().c_str(), GetId()); + // tile list is optional + if (FILE* tileList = fopen(tileListName.c_str(), "rb")) + { + u_map_magic mapMagic = { }; + uint32 versionMagic = { }; + uint32 build; + char tilesData[MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS] = { }; + if (fread(mapMagic.data(), mapMagic.size(), 1, tileList) == 1 + && mapMagic == MapMagic + && fread(&versionMagic, sizeof(versionMagic), 1, tileList) == 1 + && versionMagic == MapVersionMagic + && fread(&build, sizeof(build), 1, tileList) == 1 + && fread(&tilesData[0], MAX_NUMBER_OF_GRIDS * MAX_NUMBER_OF_GRIDS, 1, tileList) == 1) + { + _gridFileExists = std::bitset<MAX_NUMBER_OF_GRIDS* MAX_NUMBER_OF_GRIDS>(tilesData, std::size(tilesData)); + fclose(tileList); + return; + } + + fclose(tileList); + } + + for (uint32 gx = 0; gx < MAX_NUMBER_OF_GRIDS; ++gx) + for (uint32 gy = 0; gy < MAX_NUMBER_OF_GRIDS; ++gy) + _gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy] = ExistMap(GetId(), gx, gy, false); +} + +bool TerrainInfo::ExistMap(uint32 mapid, int32 gx, int32 gy, bool log /*= true*/) +{ + 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) + { + if (log) + { + 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 != MapMagic || header.versionMagic != MapVersionMagic) + { + if (log) + TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s v%u), %.*s v%u 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.data(), header.versionMagic, 4, MapMagic.data(), MapVersionMagic); + } + else + ret = true; + } + fclose(file); + } + + return ret; +} + +bool TerrainInfo::ExistVMap(uint32 mapid, int32 gx, int32 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; + case VMAP::LoadResult::ReadFromFileFailed: + TC_LOG_ERROR("maps", "VMap file '%s' couldn't be loaded", (sWorld->GetDataPath() + "vmaps/" + name).c_str()); + TC_LOG_ERROR("maps", "This is because VMAP files are corrupted, please re-extract the maps with the tools compiled with this module."); + return false; + case VMAP::LoadResult::DisabledInConfig: + TC_LOG_ERROR("maps", "VMap file '%s' couldn't be loaded", (sWorld->GetDataPath() + "vmaps/" + name).c_str()); + TC_LOG_ERROR("maps", "This is because VMAP is disabled in config file."); + return false; + } + } + } + + return true; +} + +bool TerrainInfo::HasChildTerrainGridFile(uint32 mapId, int32 gx, int32 gy) const +{ + auto childMapItr = std::find_if(_childTerrain.begin(), _childTerrain.end(), [mapId](std::shared_ptr<TerrainInfo> const& childTerrain) { return childTerrain->GetId() == mapId; }); + return childMapItr != _childTerrain.end() && (*childMapItr)->_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy]; +} + +void TerrainInfo::AddChildTerrain(std::shared_ptr<TerrainInfo> childTerrain) +{ + childTerrain->_parentTerrain = this; + _childTerrain.emplace_back(std::move(childTerrain)); +} + +void TerrainInfo::LoadMapAndVMap(int32 gx, int32 gy) +{ + if (++_referenceCountFromMap[gx][gy] != 1) // check if already loaded + return; + + std::lock_guard<std::mutex> lock(_loadMutex); + LoadMapAndVMapImpl(gx, gy); +} + +void TerrainInfo::LoadMapAndVMapImpl(int32 gx, int32 gy) +{ + LoadMap(gx, gy); + LoadVMap(gx, gy); + LoadMMap(gx, gy); + + for (std::shared_ptr<TerrainInfo> const& childTerrain : _childTerrain) + childTerrain->LoadMapAndVMapImpl(gx, gy); +} + +void TerrainInfo::LoadMap(int32 gx, int32 gy) +{ + if (_gridMap[gx][gy]) + return; + + if (!_gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy]) + return; + + // map file name + std::string fileName = Trinity::StringFormat("%smaps/%04u_%02u_%02u.map", sWorld->GetDataPath().c_str(), GetId(), gx, gy); + TC_LOG_DEBUG("maps", "Loading map %s", fileName.c_str()); + // loading data + std::unique_ptr<GridMap> gridMap = std::make_unique<GridMap>(); + GridMap::LoadResult gridMapLoadResult = gridMap->loadData(fileName.c_str()); + if (gridMapLoadResult == GridMap::LoadResult::Ok) + _gridMap[gx][gy] = std::move(gridMap); + else + _gridFileExists[gx * MAX_NUMBER_OF_GRIDS + gy] = false; + + if (gridMapLoadResult == GridMap::LoadResult::InvalidFile) + TC_LOG_ERROR("maps", "Error loading map file: %s", fileName.c_str()); +} + +void TerrainInfo::LoadVMap(int32 gx, int32 gy) +{ + if (!VMAP::VMapFactory::createOrGetVMapManager()->isMapLoadingEnabled()) + return; + // x and y are swapped !! + VMAP::LoadResult vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld->GetDataPath() + "vmaps").c_str(), GetId(), gx, gy); + switch (vmapLoadResult) + { + case VMAP::LoadResult::Success: + 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; + default: + 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::LoadResult::DisabledInConfig: + 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 TerrainInfo::LoadMMap(int32 gx, int32 gy) +{ + if (!DisableMgr::IsPathfindingEnabled(GetId())) + return; + + bool mmapLoadResult = MMAP::MMapFactory::createOrGetMMapManager()->loadMap(sWorld->GetDataPath(), GetId(), gx, gy); + + if (mmapLoadResult) + TC_LOG_DEBUG("mmaps.tiles", "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_WARN("mmaps.tiles", "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 TerrainInfo::UnloadMap(int32 gx, int32 gy) +{ + --_referenceCountFromMap[gx][gy]; + // unload later +} + +void TerrainInfo::UnloadMapImpl(int32 gx, int32 gy) +{ + _gridMap[gx][gy] = nullptr; + VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gx, gy); + MMAP::MMapFactory::createOrGetMMapManager()->unloadMap(GetId(), gx, gy); + + for (std::shared_ptr<TerrainInfo> const& childTerrain : _childTerrain) + childTerrain->UnloadMapImpl(gx, gy); +} + +GridMap* TerrainInfo::GetGrid(uint32 mapId, float x, float y, bool loadIfMissing /*= true*/) +{ + // half opt method + int32 gx = (int)(CENTER_GRID_ID - x / SIZE_OF_GRIDS); //grid x + int32 gy = (int)(CENTER_GRID_ID - y / SIZE_OF_GRIDS); //grid y + + // ensure GridMap is loaded + if (!_gridMap[gx][gy] && loadIfMissing) + { + std::lock_guard<std::mutex> lock(_loadMutex); + LoadMapAndVMapImpl(gx, gy); + } + + GridMap* grid = _gridMap[gx][gy].get(); + if (mapId != GetId()) + { + auto childMapItr = std::find_if(_childTerrain.begin(), _childTerrain.end(), [mapId](std::shared_ptr<TerrainInfo> const& childTerrain) { return childTerrain->GetId() == mapId; }); + if (childMapItr != _childTerrain.end() && (*childMapItr)->_gridMap[gx][gy]) + grid = (*childMapItr)->GetGrid(mapId, x, y, false); + } + + return grid; +} + +void TerrainInfo::CleanUpGrids(uint32 diff) +{ + _cleanupTimer.Update(diff); + if (!_cleanupTimer.Passed()) + return; + + // delete those GridMap objects which have refcount = 0 + for (int32 x = 0; x < MAX_NUMBER_OF_GRIDS; ++x) + for (int32 y = 0; y < MAX_NUMBER_OF_GRIDS; ++y) + if (_gridMap[x][y] && !_referenceCountFromMap[x][y]) + UnloadMapImpl(x, y); + + _cleanupTimer.Reset(CleanupInterval); +} + +static bool IsInWMOInterior(uint32 mogpFlags) +{ + return (mogpFlags & 0x2000) != 0; +} + +void TerrainInfo::GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, map_liquidHeaderTypeFlags reqLiquidType, float collisionHeight, DynamicMapTree const* dynamicMapTree) +{ + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + VMAP::AreaAndLiquidData vmapData; + VMAP::AreaAndLiquidData dynData; + VMAP::AreaAndLiquidData* wmoData = nullptr; + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); + GridMap* gmap = GetGrid(terrainMapId, x, y); + vmgr->getAreaAndLiquidData(terrainMapId, x, y, z, AsUnderlyingType(reqLiquidType), vmapData); + if (dynamicMapTree) + dynamicMapTree->getAreaAndLiquidData(x, y, z, phaseShift, AsUnderlyingType(reqLiquidType), dynData); + + uint32 gridAreaId = 0; + float gridMapHeight = INVALID_HEIGHT; + if (gmap) + { + gridAreaId = gmap->getArea(x, y); + gridMapHeight = gmap->getHeight(x, y); + } + + bool useGridLiquid = true; + + // floor is the height we are closer to (but only if above) + data.floorZ = VMAP_INVALID_HEIGHT; + if (gridMapHeight > INVALID_HEIGHT && G3D::fuzzyGe(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE)) + data.floorZ = gridMapHeight; + if (vmapData.floorZ > VMAP_INVALID_HEIGHT && + G3D::fuzzyGe(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) && + (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapData.floorZ > gridMapHeight)) + { + data.floorZ = vmapData.floorZ; + wmoData = &vmapData; + } + // NOTE: Objects will not detect a case when a wmo providing area/liquid despawns from under them + // but this is fine as these kind of objects are not meant to be spawned and despawned a lot + // example: Lich King platform + if (dynData.floorZ > VMAP_INVALID_HEIGHT && + G3D::fuzzyGe(z, dynData.floorZ - GROUND_HEIGHT_TOLERANCE) && + (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > gridMapHeight) && + (G3D::fuzzyLt(z, vmapData.floorZ - GROUND_HEIGHT_TOLERANCE) || dynData.floorZ > vmapData.floorZ)) + { + data.floorZ = dynData.floorZ; + wmoData = &dynData; + } + + if (wmoData) + { + if (wmoData->areaInfo) + { + data.areaInfo.emplace(wmoData->areaInfo->adtId, wmoData->areaInfo->rootId, wmoData->areaInfo->groupId, wmoData->areaInfo->mogpFlags); + // wmo found + WMOAreaTableEntry const* wmoEntry = sDB2Manager.GetWMOAreaTable(wmoData->areaInfo->rootId, wmoData->areaInfo->adtId, wmoData->areaInfo->groupId); + data.outdoors = (wmoData->areaInfo->mogpFlags & 0x8) != 0; + if (wmoEntry) + { + data.areaId = wmoEntry->AreaTableID; + if (wmoEntry->Flags & 4) + data.outdoors = true; + else if (wmoEntry->Flags & 2) + data.outdoors = false; + } + + if (!data.areaId) + data.areaId = gridAreaId; + + useGridLiquid = !IsInWMOInterior(wmoData->areaInfo->mogpFlags); + } + } + else + { + data.outdoors = true; + data.areaId = gridAreaId; + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId)) + data.outdoors = (areaEntry->Flags[0] & (AREA_FLAG_INSIDE | AREA_FLAG_OUTSIDE)) != AREA_FLAG_INSIDE; + } + + if (!data.areaId) + data.areaId = sMapStore.AssertEntry(GetId())->AreaTableID; + + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(data.areaId); + + // liquid processing + data.liquidStatus = LIQUID_MAP_NO_WATER; + if (wmoData && wmoData->liquidInfo && wmoData->liquidInfo->level > wmoData->floorZ) + { + uint32 liquidType = wmoData->liquidInfo->type; + if (GetId() == 530 && liquidType == 2) // gotta love hacks + liquidType = 15; + + uint32 liquidFlagType = 0; + if (LiquidTypeEntry const* liquidData = sLiquidTypeStore.LookupEntry(liquidType)) + liquidFlagType = liquidData->SoundBank; + + if (liquidType && liquidType < 21 && areaEntry) + { + uint32 overrideLiquid = areaEntry->LiquidTypeID[liquidFlagType]; + if (!overrideLiquid && areaEntry->ParentAreaID) + { + AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(areaEntry->ParentAreaID); + if (zoneEntry) + overrideLiquid = zoneEntry->LiquidTypeID[liquidFlagType]; + } + + if (LiquidTypeEntry const* overrideData = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + liquidType = overrideLiquid; + liquidFlagType = overrideData->SoundBank; + } + } + + data.liquidInfo.emplace(); + data.liquidInfo->level = wmoData->liquidInfo->level; + data.liquidInfo->depth_level = wmoData->floorZ; + data.liquidInfo->entry = liquidType; + data.liquidInfo->type_flags = map_liquidHeaderTypeFlags(1 << liquidFlagType); + + float delta = wmoData->liquidInfo->level - z; + if (delta > collisionHeight) + data.liquidStatus = LIQUID_MAP_UNDER_WATER; + else if (delta > 0.0f) + data.liquidStatus = LIQUID_MAP_IN_WATER; + else if (delta > -0.1f) + data.liquidStatus = LIQUID_MAP_WATER_WALK; + else + data.liquidStatus = LIQUID_MAP_ABOVE_WATER; + } + // look up liquid data from grid map + if (gmap && useGridLiquid) + { + LiquidData gridMapLiquid; + ZLiquidStatus gridMapStatus = gmap->GetLiquidStatus(x, y, z, reqLiquidType, &gridMapLiquid, collisionHeight); + if (gridMapStatus != LIQUID_MAP_NO_WATER && (!wmoData || gridMapLiquid.level > wmoData->floorZ)) + { + if (GetId() == 530 && gridMapLiquid.entry == 2) + gridMapLiquid.entry = 15; + data.liquidInfo = gridMapLiquid; + data.liquidStatus = gridMapStatus; + } + } +} + +ZLiquidStatus TerrainInfo::GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, map_liquidHeaderTypeFlags ReqLiquidType, LiquidData* data, float collisionHeight) +{ + 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; + uint32 mogpFlags = 0; + bool useGridLiquid = true; + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); + if (vmgr->GetLiquidLevel(terrainMapId, x, y, z, AsUnderlyingType(ReqLiquidType), liquid_level, ground_level, liquid_type, mogpFlags)) + { + useGridLiquid = !IsInWMOInterior(mogpFlags); + TC_LOG_DEBUG("maps", "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 && G3D::fuzzyGe(z, ground_level - GROUND_HEIGHT_TOLERANCE)) + { + // 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->SoundBank; + + if (liquid_type && liquid_type < 21) + { + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(phaseShift, x, y, z))) + { + uint32 overrideLiquid = area->LiquidTypeID[liquidFlagType]; + if (!overrideLiquid && area->ParentAreaID) + { + area = sAreaTableStore.LookupEntry(area->ParentAreaID); + if (area) + overrideLiquid = area->LiquidTypeID[liquidFlagType]; + } + + if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) + { + liquid_type = overrideLiquid; + liquidFlagType = liq->SoundBank; + } + } + } + + data->level = liquid_level; + data->depth_level = ground_level; + + data->entry = liquid_type; + data->type_flags = map_liquidHeaderTypeFlags(1 << liquidFlagType); + } + + float delta = liquid_level - z; + + // Get position delta + if (delta > collisionHeight) // 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 (useGridLiquid) + { + if (GridMap const* gmap = GetGrid(terrainMapId, x, y)) + { + LiquidData map_data; + ZLiquidStatus map_result = gmap->GetLiquidStatus(x, y, z, ReqLiquidType, &map_data, collisionHeight); + // 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; +} + +bool TerrainInfo::GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId, DynamicMapTree const* dynamicMapTree) +{ + float vmap_z = z; + float dynamic_z = z; + float check_z = z; + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + uint32 vflags; + int32 vadtId; + int32 vrootId; + int32 vgroupId; + uint32 dflags; + int32 dadtId; + int32 drootId; + int32 dgroupId; + + bool hasVmapAreaInfo = vmgr->getAreaInfo(terrainMapId, x, y, vmap_z, vflags, vadtId, vrootId, vgroupId); + bool hasDynamicAreaInfo = dynamicMapTree ? dynamicMapTree->getAreaInfo(x, y, dynamic_z, phaseShift, dflags, dadtId, drootId, dgroupId) : false; + auto useVmap = [&]() { check_z = vmap_z; mogpflags = vflags; adtId = vadtId; rootId = vrootId; groupId = vgroupId; }; + auto useDyn = [&]() { check_z = dynamic_z; mogpflags = dflags; adtId = dadtId; rootId = drootId; groupId = dgroupId; }; + + if (hasVmapAreaInfo) + { + if (hasDynamicAreaInfo && dynamic_z > vmap_z) + useDyn(); + else + useVmap(); + } + else if (hasDynamicAreaInfo) + { + useDyn(); + } + + if (hasVmapAreaInfo || hasDynamicAreaInfo) + { + // check if there's terrain between player height and object height + if (GridMap* gmap = GetGrid(terrainMapId, 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 > check_z) + return false; + } + return true; + } + return false; +} + +uint32 TerrainInfo::GetAreaId(PhaseShift const& phaseShift, float x, float y, float z, DynamicMapTree const* dynamicMapTree) +{ + uint32 mogpFlags; + int32 adtId, rootId, groupId; + float vmapZ = z; + bool hasVmapArea = GetAreaInfo(phaseShift, x, y, vmapZ, mogpFlags, adtId, rootId, groupId, dynamicMapTree); + + uint32 gridAreaId = 0; + float gridMapHeight = INVALID_HEIGHT; + if (GridMap* gmap = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) + { + gridAreaId = gmap->getArea(x, y); + gridMapHeight = gmap->getHeight(x, y); + } + + uint32 areaId = 0; + + // floor is the height we are closer to (but only if above) + if (hasVmapArea && G3D::fuzzyGe(z, vmapZ - GROUND_HEIGHT_TOLERANCE) && (G3D::fuzzyLt(z, gridMapHeight - GROUND_HEIGHT_TOLERANCE) || vmapZ > gridMapHeight)) + { + // wmo found + if (WMOAreaTableEntry const* wmoEntry = sDB2Manager.GetWMOAreaTable(rootId, adtId, groupId)) + areaId = wmoEntry->AreaTableID; + + if (!areaId) + areaId = gridAreaId; + } + else + areaId = gridAreaId; + + if (!areaId) + areaId = sMapStore.AssertEntry(GetId())->AreaTableID; + + return areaId; +} + +uint32 TerrainInfo::GetZoneId(PhaseShift const& phaseShift, float x, float y, float z, DynamicMapTree const* dynamicMapTree) +{ + uint32 areaId = GetAreaId(phaseShift, x, y, z, dynamicMapTree); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) + if (area->ParentAreaID) + return area->ParentAreaID; + + return areaId; +} + +void TerrainInfo::GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z, DynamicMapTree const* dynamicMapTree) +{ + areaid = zoneid = GetAreaId(phaseShift, x, y, z, dynamicMapTree); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid)) + if (area->ParentAreaID) + zoneid = area->ParentAreaID; +} + +float TerrainInfo::GetMinHeight(PhaseShift const& phaseShift, float x, float y) +{ + if (GridMap const* grid = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) + return grid->getMinHeight(x, y); + + return -500.0f; +} + +float TerrainInfo::GetGridHeight(PhaseShift const& phaseShift, float x, float y) +{ + if (GridMap* gmap = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) + return gmap->getHeight(x, y); + + return VMAP_INVALID_HEIGHT_VALUE; +} + +float TerrainInfo::GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap /*= true*/, float maxSearchDist /*= DEFAULT_HEIGHT_SEARCH*/) +{ + // find raw .map surface under Z coordinates + float mapHeight = VMAP_INVALID_HEIGHT_VALUE; + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(phaseShift, this, x, y); + float gridHeight = GetGridHeight(phaseShift, x, y); + if (G3D::fuzzyGe(z, gridHeight - GROUND_HEIGHT_TOLERANCE)) + mapHeight = gridHeight; + + float vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + if (checkVMap) + { + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + if (vmgr->isHeightCalcEnabled()) + vmapHeight = vmgr->getHeight(terrainMapId, x, y, z, maxSearchDist); + } + + // 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 + + // vmap height above map height + // or if the distance of the vmap height is less the land height distance + if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z)) + return vmapHeight; + + return mapHeight; // better use .map surface height + } + + return vmapHeight; // we have only vmapHeight (if have) + } + + return mapHeight; // explicitly use map data +} + +float TerrainInfo::GetWaterLevel(PhaseShift const& phaseShift, float x, float y) +{ + if (GridMap* gmap = GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) + return gmap->getLiquidLevel(x, y); + else + return 0; +} + +bool TerrainInfo::IsInWater(PhaseShift const& phaseShift, float x, float y, float pZ, LiquidData* data) +{ + LiquidData liquid_status; + LiquidData* liquid_ptr = data ? data : &liquid_status; + return (GetLiquidStatus(phaseShift, x, y, pZ, map_liquidHeaderTypeFlags::AllLiquids, liquid_ptr) & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) != 0; +} + +bool TerrainInfo::IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z) +{ + return (GetLiquidStatus(phaseShift, x, y, z, map_liquidHeaderTypeFlags::Water | map_liquidHeaderTypeFlags::Ocean) & LIQUID_MAP_UNDER_WATER) != 0; +} + +float TerrainInfo::GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground /*= nullptr*/, bool /*swim = false*/, float collisionHeight /*= DEFAULT_COLLISION_HEIGHT*/, DynamicMapTree const* dynamicMapTree /*= nullptr*/) +{ + if (GetGrid(PhasingHandler::GetTerrainMapId(phaseShift, this, x, y), x, y)) + { + // we need ground level (including grid height version) for proper return water level in point + float ground_z = GetStaticHeight(phaseShift, x, y, z + Z_OFFSET_FIND_HEIGHT, true, 50.0f); + if (dynamicMapTree) + ground_z = std::max(ground_z, dynamicMapTree->getHeight(x, y, z + Z_OFFSET_FIND_HEIGHT, 50.0f, phaseShift)); + + if (ground) + *ground = ground_z; + + LiquidData liquid_status; + + ZLiquidStatus res = GetLiquidStatus(phaseShift, x, y, ground_z, map_liquidHeaderTypeFlags::AllLiquids, &liquid_status, collisionHeight); + switch (res) + { + case LIQUID_MAP_ABOVE_WATER: + return std::max<float>(liquid_status.level, ground_z); + case LIQUID_MAP_NO_WATER: + return ground_z; + default: + return liquid_status.level; + } + } + + return VMAP_INVALID_HEIGHT_VALUE; +} + +TerrainMgr::TerrainMgr() +{ +} + +TerrainMgr::~TerrainMgr() +{ +} + +TerrainMgr& TerrainMgr::Instance() +{ + static TerrainMgr instance; + return instance; +} + +void TerrainMgr::InitializeParentMapData(std::unordered_map<uint32, std::vector<uint32>> const& mapData) +{ + _parentMapData = mapData; +} + +std::shared_ptr<TerrainInfo> TerrainMgr::LoadTerrain(uint32 mapId) +{ + MapEntry const* entry = sMapStore.LookupEntry(mapId); + if (!entry) + return nullptr; + + while (entry->ParentMapID != -1 || entry->CosmeticParentMapID != -1) + { + uint32 parentMapId = entry->ParentMapID != -1 ? entry->ParentMapID : entry->CosmeticParentMapID; + entry = sMapStore.LookupEntry(parentMapId); + if (!entry) + break; + + mapId = parentMapId; + } + + auto itr = _terrainMaps.find(mapId); + if (itr != _terrainMaps.end()) + if (std::shared_ptr<TerrainInfo> terrain = itr->second.lock()) + return terrain; + + std::shared_ptr<TerrainInfo> terrainInfo = LoadTerrainImpl(mapId); + _terrainMaps[mapId] = terrainInfo; + return terrainInfo; +} + +void TerrainMgr::UnloadAll() +{ + _terrainMaps.clear(); +} + +void TerrainMgr::Update(uint32 diff) +{ + // global garbage collection + for (auto& [mapId, terrainRef] : _terrainMaps) + if (std::shared_ptr<TerrainInfo> terrain = terrainRef.lock()) + terrain->CleanUpGrids(diff); +} + +uint32 TerrainMgr::GetAreaId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z) +{ + if (std::shared_ptr<TerrainInfo> t = LoadTerrain(mapid)) + return t->GetAreaId(phaseShift, x, y, z); + return 0; +} + +uint32 TerrainMgr::GetZoneId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z) +{ + if (std::shared_ptr<TerrainInfo> t = LoadTerrain(mapid)) + return t->GetZoneId(phaseShift, x, y, z); + return 0; +} + +void TerrainMgr::GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) +{ + if (std::shared_ptr<TerrainInfo> t = LoadTerrain(mapid)) + t->GetZoneAndAreaId(phaseShift, zoneid, areaid, x, y, z); +} + +std::shared_ptr<TerrainInfo> TerrainMgr::LoadTerrainImpl(uint32 mapId) +{ + std::shared_ptr<TerrainInfo> rootTerrain(new TerrainInfo(mapId)); // intentionally not using make_shared, don't want control block allocated together, will be relying on weak_ptr + + rootTerrain->DiscoverGridMapFiles(); + + for (uint32 childMapId : _parentMapData[mapId]) + rootTerrain->AddChildTerrain(LoadTerrainImpl(childMapId)); + + return rootTerrain; +} + +bool TerrainMgr::ExistMapAndVMap(uint32 mapid, float x, float y) +{ + GridCoord p = Trinity::ComputeGridCoord(x, y); + + int32 gx = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord; + int32 gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord; + + return TerrainInfo::ExistMap(mapid, gx, gy) && TerrainInfo::ExistVMap(mapid, gx, gy); +} diff --git a/src/server/game/Maps/TerrainMgr.h b/src/server/game/Maps/TerrainMgr.h new file mode 100644 index 00000000000..efe77bb3c1a --- /dev/null +++ b/src/server/game/Maps/TerrainMgr.h @@ -0,0 +1,160 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TERRAIN_MGR_H +#define TERRAIN_MGR_H + +#include "Define.h" +#include "GridDefines.h" +#include "MapDefines.h" +#include "Position.h" +#include "Timer.h" +#include <atomic> +#include <bitset> +#include <memory> +#include <mutex> +#include <unordered_map> +#include <vector> + +class DynamicMapTree; +class GridMap; +class PhaseShift; + +class TerrainInfo +{ +public: + explicit TerrainInfo(uint32 mapId); + TerrainInfo(TerrainInfo const&) = delete; + TerrainInfo(TerrainInfo&&) = delete; + TerrainInfo& operator=(TerrainInfo const&) = delete; + TerrainInfo& operator=(TerrainInfo&&) = delete; + ~TerrainInfo(); + + uint32 GetId() const { return _mapId; } + char const* GetMapName() const; + + void DiscoverGridMapFiles(); + + static bool ExistMap(uint32 mapid, int32 gx, int32 gy, bool log = true); + static bool ExistVMap(uint32 mapid, int32 gx, int32 gy); + + bool HasChildTerrainGridFile(uint32 mapId, int32 gx, int32 gy) const; + void AddChildTerrain(std::shared_ptr<TerrainInfo> childTerrain); + + void LoadMapAndVMap(int32 gx, int32 gy); + +private: + void LoadMapAndVMapImpl(int32 gx, int32 gy); + void LoadMap(int32 gx, int32 gy); + void LoadVMap(int32 gx, int32 gy); + void LoadMMap(int32 gx, int32 gy); + +public: + void UnloadMap(int32 gx, int32 gy); + +private: + void UnloadMapImpl(int32 gx, int32 gy); + + GridMap* GetGrid(uint32 mapId, float x, float y, bool loadIfMissing = true); + +public: + void CleanUpGrids(uint32 diff); + + void GetFullTerrainStatusForPosition(PhaseShift const& phaseShift, float x, float y, float z, PositionFullTerrainStatus& data, map_liquidHeaderTypeFlags reqLiquidType = map_liquidHeaderTypeFlags::AllLiquids, float collisionHeight = 2.03128f, DynamicMapTree const* dynamicMapTree = nullptr); // DEFAULT_COLLISION_HEIGHT in Object.h + ZLiquidStatus GetLiquidStatus(PhaseShift const& phaseShift, float x, float y, float z, map_liquidHeaderTypeFlags ReqLiquidType, LiquidData* data = nullptr, float collisionHeight = 2.03128f); // DEFAULT_COLLISION_HEIGHT in Object.h + + bool GetAreaInfo(PhaseShift const& phaseShift, float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId, DynamicMapTree const* dynamicMapTree = nullptr); + uint32 GetAreaId(PhaseShift const& phaseShift, float x, float y, float z, DynamicMapTree const* dynamicMapTree = nullptr); + uint32 GetAreaId(PhaseShift const& phaseShift, Position const& pos, DynamicMapTree const* dynamicMapTree = nullptr) { return GetAreaId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), dynamicMapTree); } + uint32 GetZoneId(PhaseShift const& phaseShift, float x, float y, float z, DynamicMapTree const* dynamicMapTree = nullptr); + uint32 GetZoneId(PhaseShift const& phaseShift, Position const& pos, DynamicMapTree const* dynamicMapTree = nullptr) { return GetZoneId(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), dynamicMapTree); } + void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, float x, float y, float z, DynamicMapTree const* dynamicMapTree = nullptr); + void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, Position const& pos, DynamicMapTree const* dynamicMapTree = nullptr) { GetZoneAndAreaId(phaseShift, zoneid, areaid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), dynamicMapTree); } + + float GetMinHeight(PhaseShift const& phaseShift, float x, float y); + float GetGridHeight(PhaseShift const& phaseShift, float x, float y); + float GetStaticHeight(PhaseShift const& phaseShift, float x, float y, float z, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH); + float GetStaticHeight(PhaseShift const& phaseShift, Position const& pos, bool checkVMap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) { return GetStaticHeight(phaseShift, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), checkVMap, maxSearchDist); } + + float GetWaterLevel(PhaseShift const& phaseShift, float x, float y); + bool IsInWater(PhaseShift const& phaseShift, float x, float y, float z, LiquidData* data = nullptr); + bool IsUnderWater(PhaseShift const& phaseShift, float x, float y, float z); + + float GetWaterOrGroundLevel(PhaseShift const& phaseShift, float x, float y, float z, float* ground = nullptr, bool swim = false, float collisionHeight = 2.03128f, DynamicMapTree const* dynamicMapTree = nullptr); // DEFAULT_COLLISION_HEIGHT in Object.h + +private: + uint32 _mapId; + + TerrainInfo* _parentTerrain; + std::vector<std::shared_ptr<TerrainInfo>> _childTerrain; + + std::mutex _loadMutex; + std::unique_ptr<GridMap> _gridMap[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; + std::atomic<uint16> _referenceCountFromMap[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; + std::bitset<MAX_NUMBER_OF_GRIDS* MAX_NUMBER_OF_GRIDS> _gridFileExists; // cache what grids are available for this map (not including parent/child maps) + + static constexpr Milliseconds CleanupInterval = 1min; + + // global garbage collection timer + TimeTracker _cleanupTimer; +}; + +class TC_GAME_API TerrainMgr +{ + TerrainMgr(); + ~TerrainMgr(); +public: + TerrainMgr(TerrainMgr const&) = delete; + TerrainMgr(TerrainMgr&&) = delete; + TerrainMgr& operator=(TerrainMgr const&) = delete; + TerrainMgr& operator=(TerrainMgr&&) = delete; + + static TerrainMgr& Instance(); + + void InitializeParentMapData(std::unordered_map<uint32, std::vector<uint32>> const& mapData); + + std::shared_ptr<TerrainInfo> LoadTerrain(uint32 mapId); + void UnloadAll(); + + void Update(uint32 diff); + + uint32 GetAreaId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z); + uint32 GetAreaId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) { return GetAreaId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + uint32 GetAreaId(PhaseShift const& phaseShift, WorldLocation const& loc) { return GetAreaId(phaseShift, loc.GetMapId(), loc); } + + uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, float x, float y, float z); + uint32 GetZoneId(PhaseShift const& phaseShift, uint32 mapid, Position const& pos) { return GetZoneId(phaseShift, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + uint32 GetZoneId(PhaseShift const& phaseShift, WorldLocation const& loc) { return GetZoneId(phaseShift, loc.GetMapId(), loc); } + + void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z); + void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, uint32 mapid, Position const& pos) { GetZoneAndAreaId(phaseShift, zoneid, areaid, mapid, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); } + void GetZoneAndAreaId(PhaseShift const& phaseShift, uint32& zoneid, uint32& areaid, WorldLocation const& loc) { GetZoneAndAreaId(phaseShift, zoneid, areaid, loc.GetMapId(), loc); } + + static bool ExistMapAndVMap(uint32 mapid, float x, float y); + +private: + std::shared_ptr<TerrainInfo> LoadTerrainImpl(uint32 mapId); + + std::unordered_map<uint32, std::weak_ptr<TerrainInfo>> _terrainMaps; + + // parent map links + std::unordered_map<uint32, std::vector<uint32>> _parentMapData; +}; + +#define sTerrainMgr TerrainMgr::Instance() + +#endif // TERRAIN_MGR_H diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index fb64779e70f..9214b7ea2cd 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -38,7 +38,7 @@ PathGenerator::PathGenerator(WorldObject const* owner) : TC_LOG_DEBUG("maps.mmaps", "++ PathGenerator::PathGenerator for %s", _source->GetGUID().ToString().c_str()); - uint32 mapId = PhasingHandler::GetTerrainMapId(_source->GetPhaseShift(), _source->GetMap(), _source->GetPositionX(), _source->GetPositionY()); + uint32 mapId = PhasingHandler::GetTerrainMapId(_source->GetPhaseShift(), _source->GetMap()->GetTerrain(), _source->GetPositionX(), _source->GetPositionY()); if (DisableMgr::IsPathfindingEnabled(_source->GetMapId())) { MMAP::MMapManager* mmap = MMAP::MMapFactory::createOrGetMMapManager(); diff --git a/src/server/game/Phasing/PhasingHandler.cpp b/src/server/game/Phasing/PhasingHandler.cpp index 50e7afe586f..92e2995a514 100644 --- a/src/server/game/Phasing/PhasingHandler.cpp +++ b/src/server/game/Phasing/PhasingHandler.cpp @@ -29,6 +29,7 @@ #include "PhaseShift.h" #include "Player.h" #include "SpellAuraEffects.h" +#include "TerrainMgr.h" #include "Vehicle.h" #include <boost/container/flat_set.hpp> #include <boost/container/small_vector.hpp> @@ -72,7 +73,7 @@ public: for (ObjectGuid summonGuid : unit->m_SummonSlot) if (!summonGuid.IsEmpty()) - if (Creature* summon = unit->GetMap()->GetCreature(summonGuid)) + if (Creature* summon = ObjectAccessor::GetCreature(*unit, summonGuid)) if (_visited.insert(summon).second) func(summon); @@ -562,10 +563,10 @@ bool PhasingHandler::InDbPhaseShift(WorldObject const* object, uint8 phaseUseFla return object->GetPhaseShift().CanSee(phaseShift); } -uint32 PhasingHandler::GetTerrainMapId(PhaseShift const& phaseShift, Map const* map, float x, float y) +uint32 PhasingHandler::GetTerrainMapId(PhaseShift const& phaseShift, TerrainInfo const* terrain, float x, float y) { if (phaseShift.VisibleMapIds.empty()) - return map->GetId(); + return terrain->GetId(); if (phaseShift.VisibleMapIds.size() == 1) return phaseShift.VisibleMapIds.begin()->first; @@ -575,10 +576,10 @@ uint32 PhasingHandler::GetTerrainMapId(PhaseShift const& phaseShift, Map const* int32 gy = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord; for (std::pair<uint32 const, PhaseShift::VisibleMapIdRef> const& visibleMap : phaseShift.VisibleMapIds) - if (map->HasChildMapGridFile(visibleMap.first, gx, gy)) + if (terrain->HasChildTerrainGridFile(visibleMap.first, gx, gy)) return visibleMap.first; - return map->GetId(); + return terrain->GetId(); } void PhasingHandler::SetAlwaysVisible(WorldObject* object, bool apply, bool updateVisibility) diff --git a/src/server/game/Phasing/PhasingHandler.h b/src/server/game/Phasing/PhasingHandler.h index dfb8eb7aa94..25a89c829b4 100644 --- a/src/server/game/Phasing/PhasingHandler.h +++ b/src/server/game/Phasing/PhasingHandler.h @@ -23,10 +23,10 @@ #include <vector> class ChatHandler; -class Map; class ObjectGuid; class PhaseShift; class Player; +class TerrainInfo; class WorldObject; namespace WorldPackets { @@ -64,7 +64,7 @@ public: static void InitDbVisibleMapId(PhaseShift& phaseShift, int32 visibleMapId); static bool InDbPhaseShift(WorldObject const* object, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId); - static uint32 GetTerrainMapId(PhaseShift const& phaseShift, Map const* map, float x, float y); + static uint32 GetTerrainMapId(PhaseShift const& phaseShift, TerrainInfo const* terrain, float x, float y); static void SetAlwaysVisible(WorldObject* object, bool apply, bool updateVisibility); static void SetInversed(WorldObject* object, bool apply, bool updateVisibility); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 9c2821fa962..6733988ae85 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1538,42 +1538,6 @@ void ScriptMgr::OnDestroyMap(Map* map) SCR_MAP_END; } -void ScriptMgr::OnLoadGridMap(Map* map, GridMap* gmap, uint32 gx, uint32 gy) -{ - ASSERT(map); - ASSERT(gmap); - - SCR_MAP_BGN(WorldMapScript, map, itr, end, entry, IsWorldMap); - itr->second->OnLoadGridMap(map, gmap, gx, gy); - SCR_MAP_END; - - SCR_MAP_BGN(InstanceMapScript, map, itr, end, entry, IsDungeon); - itr->second->OnLoadGridMap((InstanceMap*)map, gmap, gx, gy); - SCR_MAP_END; - - SCR_MAP_BGN(BattlegroundMapScript, map, itr, end, entry, IsBattleground); - itr->second->OnLoadGridMap((BattlegroundMap*)map, gmap, gx, gy); - SCR_MAP_END; -} - -void ScriptMgr::OnUnloadGridMap(Map* map, GridMap* gmap, uint32 gx, uint32 gy) -{ - ASSERT(map); - ASSERT(gmap); - - SCR_MAP_BGN(WorldMapScript, map, itr, end, entry, IsWorldMap); - itr->second->OnUnloadGridMap(map, gmap, gx, gy); - SCR_MAP_END; - - SCR_MAP_BGN(InstanceMapScript, map, itr, end, entry, IsDungeon); - itr->second->OnUnloadGridMap((InstanceMap*)map, gmap, gx, gy); - SCR_MAP_END; - - SCR_MAP_BGN(BattlegroundMapScript, map, itr, end, entry, IsBattleground); - itr->second->OnUnloadGridMap((BattlegroundMap*)map, gmap, gx, gy); - SCR_MAP_END; -} - void ScriptMgr::OnPlayerEnterMap(Map* map, Player* player) { ASSERT(map); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 06ab3c2d0c8..672f5dc6bf0 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -41,7 +41,6 @@ class DynamicObject; class GameObject; class GameObjectAI; class Guild; -class GridMap; class Group; class InstanceMap; class InstanceScript; @@ -348,12 +347,6 @@ class MapScript : public UpdatableScript<TMap> // Called just before the map is destroyed. virtual void OnDestroy(TMap* /*map*/) { } - // Called when a grid map is loaded. - virtual void OnLoadGridMap(TMap* /*map*/, GridMap* /*gmap*/, uint32 /*gx*/, uint32 /*gy*/) { } - - // Called when a grid map is unloaded. - virtual void OnUnloadGridMap(TMap* /*map*/, GridMap* /*gmap*/, uint32 /*gx*/, uint32 /*gy*/) { } - // Called when a player enters the map. virtual void OnPlayerEnter(TMap* /*map*/, Player* /*player*/) { } @@ -1112,8 +1105,6 @@ class TC_GAME_API ScriptMgr void OnCreateMap(Map* map); void OnDestroyMap(Map* map); - void OnLoadGridMap(Map* map, GridMap* gmap, uint32 gx, uint32 gy); - void OnUnloadGridMap(Map* map, GridMap* gmap, uint32 gx, uint32 gy); void OnPlayerEnterMap(Map* map, Player* player); void OnPlayerLeaveMap(Map* map, Player* player); void OnMapUpdate(Map* map, uint32 diff); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 502afce812b..0d2fd838d75 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -89,6 +89,7 @@ #include "SmartScriptMgr.h" #include "SupportMgr.h" #include "TaxiPathGraph.h" +#include "TerrainMgr.h" #include "TransportMgr.h" #include "Unit.h" #include "UpdateTime.h" @@ -1743,15 +1744,15 @@ void World::SetInitialWorldSettings() sObjectMgr->SetHighestGuids(); ///- Check the existence of the map files for all races' startup areas. - if (!MapManager::ExistMapAndVMap(0, -6240.32f, 331.033f) - || !MapManager::ExistMapAndVMap(0, -8949.95f, -132.493f) - || !MapManager::ExistMapAndVMap(1, -618.518f, -4251.67f) - || !MapManager::ExistMapAndVMap(0, 1676.35f, 1677.45f) - || !MapManager::ExistMapAndVMap(1, 10311.3f, 832.463f) - || !MapManager::ExistMapAndVMap(1, -2917.58f, -257.98f) + if (!TerrainMgr::ExistMapAndVMap(0, -6240.32f, 331.033f) + || !TerrainMgr::ExistMapAndVMap(0, -8949.95f, -132.493f) + || !TerrainMgr::ExistMapAndVMap(1, -618.518f, -4251.67f) + || !TerrainMgr::ExistMapAndVMap(0, 1676.35f, 1677.45f) + || !TerrainMgr::ExistMapAndVMap(1, 10311.3f, 832.463f) + || !TerrainMgr::ExistMapAndVMap(1, -2917.58f, -257.98f) || (m_int_configs[CONFIG_EXPANSION] && ( - !MapManager::ExistMapAndVMap(530, 10349.6f, -6357.29f) || - !MapManager::ExistMapAndVMap(530, -3961.64f, -13931.2f)))) + !TerrainMgr::ExistMapAndVMap(530, 10349.6f, -6357.29f) || + !TerrainMgr::ExistMapAndVMap(530, -3961.64f, -13931.2f)))) { TC_LOG_FATAL("server.loading", "Unable to load critical files - server shutting down !!!"); exit(1); @@ -1821,7 +1822,7 @@ void World::SetInitialWorldSettings() mapData[mapEntry->CosmeticParentMapID].push_back(mapEntry->ID); } - sMapMgr->InitializeParentMapData(mapData); + sTerrainMgr.InitializeParentMapData(mapData); vmmgr2->InitializeThreadUnsafe(mapData); @@ -2737,6 +2738,11 @@ void World::Update(uint32 diff) sMapMgr->Update(diff); } + { + TC_METRIC_TIMER("world_update_time", TC_METRIC_TAG("type", "Terrain data cleanup")); + sTerrainMgr.Update(diff); + } + if (sWorld->getBoolConfig(CONFIG_AUTOBROADCAST)) { if (m_timers[WUPDATE_AUTOBROADCAST].Passed()) diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 8977ec55f77..b658023cfd5 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -34,6 +34,7 @@ EndScriptData */ #include "Player.h" #include "RBAC.h" #include "SupportMgr.h" +#include "TerrainMgr.h" #include "Transport.h" #include "Util.h" #include "WorldSession.h" @@ -230,8 +231,8 @@ public: else player->SaveRecallPosition(); // save only in non-flight case - Map* map = sMapMgr->CreateBaseMap(mapId); - float z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); + std::shared_ptr<TerrainInfo> terrain = sTerrainMgr.LoadTerrain(mapId); + float z = std::max(terrain->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), terrain->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); player->TeleportTo(mapId, x, y, z, player->GetOrientation()); return true; @@ -289,8 +290,8 @@ public: else player->SaveRecallPosition(); // save only in non-flight case - Map* map = sMapMgr->CreateBaseMap(mapId); - z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); + std::shared_ptr<TerrainInfo> terrain = sTerrainMgr.LoadTerrain(mapId); + z = std::max(terrain->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), terrain->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); player->TeleportTo(mapId, x, y, z, 0.0f); return true; @@ -343,10 +344,10 @@ public: x /= 100.0f; y /= 100.0f; - Map* map = sMapMgr->CreateBaseMap(zoneEntry->ContinentID); + std::shared_ptr<TerrainInfo> terrain = sTerrainMgr.LoadTerrain(zoneEntry->ContinentID); if (!sDB2Manager.Zone2MapCoordinates(areaEntry->ParentAreaID ? uint32(areaEntry->ParentAreaID) : areaId, x, y)) { - handler->PSendSysMessage(LANG_INVALID_ZONE_MAP, areaId, areaEntry->AreaName[handler->GetSessionDbcLocale()], map->GetId(), map->GetMapName()); + handler->PSendSysMessage(LANG_INVALID_ZONE_MAP, areaId, areaEntry->AreaName[handler->GetSessionDbcLocale()], terrain->GetId(), terrain->GetMapName()); handler->SetSentErrorMessage(true); return false; } @@ -364,7 +365,7 @@ public: else player->SaveRecallPosition(); // save only in non-flight case - float z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); + float z = std::max(terrain->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), terrain->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); player->TeleportTo(zoneEntry->ContinentID, x, y, z, player->GetOrientation()); return true; @@ -392,8 +393,8 @@ public: handler->SetSentErrorMessage(true); return false; } - Map* map = sMapMgr->CreateBaseMap(mapId); - z = std::max(map->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), map->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); + std::shared_ptr<TerrainInfo> terrain = sTerrainMgr.LoadTerrain(mapId); + z = std::max(terrain->GetStaticHeight(PhasingHandler::GetEmptyPhaseShift(), x, y, MAX_HEIGHT), terrain->GetWaterLevel(PhasingHandler::GetEmptyPhaseShift(), x, y)); } return DoTeleport(handler, { x, y, *z, o.value_or(0.0f) }, mapId); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 9ef49a442ce..38fda7ba51c 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -32,8 +32,6 @@ #include "IPLocation.h" #include "Item.h" #include "Language.h" -#include "Log.h" -#include "MapManager.h" #include "MiscPackets.h" #include "MMapFactory.h" #include "MotionMaster.h" @@ -46,6 +44,7 @@ #include "SpellAuras.h" #include "SpellHistory.h" #include "SpellMgr.h" +#include "TerrainMgr.h" #include "Transport.h" #include "Weather.h" #include "World.h" @@ -262,8 +261,8 @@ public: int gridX = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.x_coord; int gridY = (MAX_NUMBER_OF_GRIDS - 1) - gridCoord.y_coord; - uint32 haveMap = Map::ExistMap(mapId, gridX, gridY) ? 1 : 0; - uint32 haveVMap = Map::ExistVMap(mapId, gridX, gridY) ? 1 : 0; + uint32 haveMap = TerrainInfo::ExistMap(mapId, gridX, gridY) ? 1 : 0; + uint32 haveVMap = TerrainInfo::ExistVMap(mapId, gridX, gridY) ? 1 : 0; uint32 haveMMap = (DisableMgr::IsPathfindingEnabled(mapId) && MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(handler->GetSession()->GetPlayer()->GetMapId())) ? 1 : 0; if (haveVMap) diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp index 2b7f266a2df..ad43389593e 100644 --- a/src/server/scripts/Commands/cs_mmaps.cpp +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -142,7 +142,7 @@ public: handler->PSendSysMessage("tileloc [%i, %i]", gy, gx); // calculate navmesh tile location - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMap(), x, y); + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMap()->GetTerrain(), x, y); dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(terrainMapId); dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(terrainMapId, player->GetInstanceId()); if (!navmesh || !navmeshquery) @@ -193,7 +193,7 @@ public: static bool HandleMmapLoadedTilesCommand(ChatHandler* handler, char const* /*args*/) { Player* player = handler->GetSession()->GetPlayer(); - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMap(), player->GetPositionX(), player->GetPositionY()); + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMap()->GetTerrain(), player->GetPositionX(), player->GetPositionY()); dtNavMesh const* navmesh = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMesh(terrainMapId); dtNavMeshQuery const* navmeshquery = MMAP::MMapFactory::createOrGetMMapManager()->GetNavMeshQuery(terrainMapId, player->GetInstanceId()); if (!navmesh || !navmeshquery) @@ -219,7 +219,7 @@ public: static bool HandleMmapStatsCommand(ChatHandler* handler, char const* /*args*/) { Player* player = handler->GetSession()->GetPlayer(); - uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMap(), player->GetPositionX(), player->GetPositionY()); + uint32 terrainMapId = PhasingHandler::GetTerrainMapId(player->GetPhaseShift(), player->GetMap()->GetTerrain(), player->GetPositionX(), player->GetPositionY()); handler->PSendSysMessage("mmap stats:"); handler->PSendSysMessage(" global mmap pathfinding is %sabled", DisableMgr::IsPathfindingEnabled(player->GetMapId()) ? "en" : "dis"); diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp index 6469a31249d..b538507732e 100644 --- a/src/server/scripts/Commands/cs_tele.cpp +++ b/src/server/scripts/Commands/cs_tele.cpp @@ -34,6 +34,7 @@ EndScriptData */ #include "PhasingHandler.h" #include "Player.h" #include "RBAC.h" +#include "TerrainMgr.h" #include "WorldSession.h" using namespace Trinity::ChatCommands; @@ -166,7 +167,7 @@ public: handler->PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), handler->GetTrinityString(LANG_OFFLINE), locationName.c_str()); - Player::SavePositionInDB({ mapId, pos }, sMapMgr->GetZoneId(PhasingHandler::GetEmptyPhaseShift(), { mapId, pos }), player.GetGUID(), nullptr); + Player::SavePositionInDB({ mapId, pos }, sTerrainMgr.GetZoneId(PhasingHandler::GetEmptyPhaseShift(), { mapId, pos }), player.GetGUID(), nullptr); } return true; diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index bad72919f6a..a6da765c21c 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -48,6 +48,7 @@ #include "ScriptReloadMgr.h" #include "SecretMgr.h" #include "TCSoap.h" +#include "TerrainMgr.h" #include "World.h" #include "WorldSocket.h" #include "WorldSocketMgr.h" @@ -317,6 +318,7 @@ extern int main(int argc, char** argv) sInstanceSaveMgr->Unload(); sOutdoorPvPMgr->Die(); // unload it before MapManager sMapMgr->UnloadAll(); // unload all grids (including locked in memory) + sTerrainMgr.UnloadAll(); }); // Start the Remote Access port (acceptor) if enabled |