a3dc7b3f48
hondacrx: - Initial commit: Switch to .Net Core 2.0 - Fix build and removed not needed files Fabi: - Updated solution platforms. - Changed folder structure. - Change library target framework to netstandard2.0. - Updated solution platforms again... - Removed windows specific kernel32 function usage (Ctrl-C handler).
582 lines
24 KiB
C#
582 lines
24 KiB
C#
/*
|
|
* Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
|
|
*
|
|
* 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 3 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/>.
|
|
*/
|
|
|
|
using Framework.Constants;
|
|
using Game.DataStorage;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.Contracts;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
namespace Game
|
|
{
|
|
public class MMapManager : Singleton<MMapManager>
|
|
{
|
|
MMapManager() { }
|
|
|
|
const string MAP_FILE_NAME_FORMAT = "{0}/mmaps/{1:D4}.mmap";
|
|
const string TILE_FILE_NAME_FORMAT = "{0}/mmaps/{1:D4}{2:D2}{3:D2}.mmtile";
|
|
|
|
public void Initialize()
|
|
{
|
|
foreach (MapRecord mapEntry in CliDB.MapStorage.Values)
|
|
{
|
|
if (mapEntry.ParentMapID != -1)
|
|
phaseMapData.Add((uint)mapEntry.ParentMapID, mapEntry.Id);
|
|
}
|
|
}
|
|
|
|
MMapData GetMMapData(uint mapId)
|
|
{
|
|
return loadedMMaps.LookupByKey(mapId);
|
|
}
|
|
|
|
bool loadMapData(uint mapId)
|
|
{
|
|
// we already have this map loaded?
|
|
if (loadedMMaps.ContainsKey(mapId) && loadedMMaps[mapId] != null)
|
|
return true;
|
|
|
|
// load and init dtNavMesh - read parameters from file
|
|
string filename = string.Format(MAP_FILE_NAME_FORMAT, Global.WorldMgr.GetDataPath(), mapId);
|
|
if (!File.Exists(filename))
|
|
{
|
|
Log.outError(LogFilter.Maps, "Could not open mmap file {0}", filename);
|
|
return false;
|
|
}
|
|
|
|
using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read), Encoding.UTF8))
|
|
{
|
|
Detour.dtNavMeshParams Params = new Detour.dtNavMeshParams();
|
|
Params.orig[0] = reader.ReadSingle();
|
|
Params.orig[1] = reader.ReadSingle();
|
|
Params.orig[2] = reader.ReadSingle();
|
|
|
|
Params.tileWidth = reader.ReadSingle();
|
|
Params.tileHeight = reader.ReadSingle();
|
|
Params.maxTiles = reader.ReadInt32();
|
|
Params.maxPolys = reader.ReadInt32();
|
|
|
|
Detour.dtNavMesh mesh = new Detour.dtNavMesh();
|
|
if (Detour.dtStatusFailed(mesh.init(Params)))
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP:loadMapData: Failed to initialize dtNavMesh for mmap {0:D4} from file {1}", mapId, filename);
|
|
return false;
|
|
}
|
|
|
|
Log.outInfo(LogFilter.Maps, "MMAP:loadMapData: Loaded {0:D4}.mmap", mapId);
|
|
|
|
// store inside our map list
|
|
loadedMMaps[mapId] = new MMapData(mesh, mapId);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
uint packTileID(int x, int y)
|
|
{
|
|
return (uint)(x << 16 | y);
|
|
}
|
|
|
|
public bool loadMap(uint mapId, int x, int y)
|
|
{
|
|
// make sure the mmap is loaded and ready to load tiles
|
|
if (!loadMapData(mapId))
|
|
return false;
|
|
|
|
// get this mmap data
|
|
MMapData mmap = loadedMMaps[mapId];
|
|
Contract.Assert(mmap.navMesh != null);
|
|
|
|
// check if we already have this tile loaded
|
|
uint packedGridPos = packTileID(x, y);
|
|
if (mmap.loadedTileRefs.ContainsKey(packedGridPos))
|
|
return false;
|
|
|
|
// load this tile . mmaps/MMMXXYY.mmtile
|
|
string filename = string.Format(TILE_FILE_NAME_FORMAT, Global.WorldMgr.GetDataPath(), mapId, x, y);
|
|
if (!File.Exists(filename))
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMAP:loadMap: Could not open mmtile file '{0}'", filename);
|
|
return false;
|
|
}
|
|
|
|
using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
|
{
|
|
MmapTileHeader fileHeader = reader.ReadStruct<MmapTileHeader>();
|
|
Array.Reverse(fileHeader.mmapMagic);
|
|
if (new string(fileHeader.mmapMagic) != MapConst.mmapMagic)
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP:loadMap: Bad header in mmap {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
|
|
return false;
|
|
}
|
|
if (fileHeader.mmapVersion != MapConst.mmapVersion)
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP:loadMap: {0:D4}{1:D2}{2:D2}.mmtile was built with generator v{3}, expected v{4}",
|
|
mapId, x, y, fileHeader.mmapVersion, MapConst.mmapVersion);
|
|
return false;
|
|
}
|
|
|
|
var bytes = reader.ReadBytes((int)fileHeader.size);
|
|
|
|
Detour.dtRawTileData data = new Detour.dtRawTileData();
|
|
data.FromBytes(bytes, 0);
|
|
|
|
ulong tileRef = 0;
|
|
// memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
|
|
if (Detour.dtStatusSucceed(mmap.navMesh.addTile(data, 0, 0, ref tileRef)))
|
|
{
|
|
mmap.loadedTileRefs.Add(packedGridPos, tileRef);
|
|
++loadedTiles;
|
|
Log.outInfo(LogFilter.Maps, "MMAP:loadMap: Loaded mmtile {0:D4}[{1:D2}, {2:D2}]", mapId, x, y);
|
|
|
|
var phasedMaps = phaseMapData.LookupByKey(mapId);
|
|
if (!phasedMaps.Empty())
|
|
{
|
|
mmap.AddBaseTile(packedGridPos, data, fileHeader, fileHeader.size);
|
|
LoadPhaseTiles(phasedMaps, x, y);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Log.outError(LogFilter.Maps, "MMAP:loadMap: Could not load {0:D4}{1:D2}{2:D2}.mmtile into navmesh", mapId, x, y);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
PhasedTile LoadTile(uint mapId, int x, int y)
|
|
{
|
|
// load this tile . mmaps/MMMXXYY.mmtile
|
|
string filename = string.Format(TILE_FILE_NAME_FORMAT, Global.WorldMgr.GetDataPath(), mapId, x, y);
|
|
if (!File.Exists(filename))
|
|
{
|
|
// Not all tiles have phased versions, don't flood this msg
|
|
return null;
|
|
}
|
|
PhasedTile pTile = new PhasedTile();
|
|
|
|
using (BinaryReader reader = new BinaryReader(new FileStream(filename, FileMode.Open, FileAccess.Read)))
|
|
{
|
|
// read header
|
|
pTile.fileHeader = reader.ReadStruct<MmapTileHeader>();
|
|
Array.Reverse(pTile.fileHeader.mmapMagic);
|
|
if (new string(pTile.fileHeader.mmapMagic) != MapConst.mmapMagic)
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP.LoadTile: Bad header in mmap {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
|
|
return null;
|
|
}
|
|
|
|
if (pTile.fileHeader.mmapVersion != MapConst.mmapVersion)
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP:LoadTile: {0:D4}{1:D2}{2:D2}.mmtile was built with generator v{3}, expected v{4}", mapId, x, y, pTile.fileHeader.mmapVersion, MapConst.mmapVersion);
|
|
return null;
|
|
}
|
|
|
|
pTile.data = new Detour.dtRawTileData();
|
|
pTile.data.FromBytes(reader.ReadBytes((int)pTile.fileHeader.size), 0);
|
|
|
|
if (pTile.data.ToBytes().Length == 0)
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP.LoadTile: Bad header or data in mmap {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return pTile;
|
|
}
|
|
|
|
void LoadPhaseTiles(List<uint> phasedMapData, int x, int y)
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMAP.LoadPhaseTiles: Loading phased mmtiles for map {0}, X: {1}, Y: {2}", phasedMapData.FirstOrDefault(), x, y);
|
|
|
|
uint packedGridPos = packTileID(x, y);
|
|
|
|
foreach (uint phaseMapId in phasedMapData)
|
|
{
|
|
PhasedTile data = LoadTile(phaseMapId, x, y);
|
|
// only a few tiles have terrain swaps, do not write error for them
|
|
if (data != null)
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMAP.LoadPhaseTiles: Loaded phased {0:D4}{1:D2}{2:D2}.mmtile for root phase map {3}", phaseMapId, x, y, phasedMapData.FirstOrDefault());
|
|
if (!_phaseTiles.ContainsKey(phaseMapId))
|
|
_phaseTiles[phaseMapId] = new Dictionary<uint, PhasedTile>();
|
|
|
|
_phaseTiles[phaseMapId][packedGridPos] = data;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UnloadPhaseTile(List<uint> phasedMapData, int x, int y)
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMAP.UnloadPhaseTile: Unloading phased mmtile for map {0}, X: {1}, Y: {2}", phasedMapData.FirstOrDefault(), x, y);
|
|
|
|
uint packedGridPos = packTileID(x, y);
|
|
|
|
foreach (uint phaseMapId in phasedMapData.ToList())
|
|
{
|
|
var phasedTileDic = _phaseTiles.LookupByKey(phaseMapId);
|
|
if (phasedTileDic == null)
|
|
continue;
|
|
|
|
var phaseTile = phasedTileDic.LookupByKey(packedGridPos);
|
|
if (phaseTile != null)
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMAP.UnloadPhaseTile: Unloaded phased {0:D4}{1:D2}{2:D2}.mmtile for root phase map {3}", phaseMapId, x, y, phasedMapData.FirstOrDefault());
|
|
phasedTileDic.Remove(packedGridPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool unloadMap(uint mapId, uint x, uint y)
|
|
{
|
|
// check if we have this map loaded
|
|
MMapData mmap = GetMMapData(mapId);
|
|
if (mmap == null)
|
|
{
|
|
// file may not exist, therefore not loaded
|
|
Log.outDebug(LogFilter.Maps, "MMAP:unloadMap: Asked to unload not loaded navmesh map. {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
|
|
return false;
|
|
}
|
|
|
|
// check if we have this tile loaded
|
|
uint packedGridPos = packTileID((int)x, (int)y);
|
|
if (!mmap.loadedTileRefs.ContainsKey(packedGridPos))
|
|
{
|
|
// file may not exist, therefore not loaded
|
|
Log.outDebug(LogFilter.Maps, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. {0:D4}{1:D2}{2:D2}.mmtile", mapId, x, y);
|
|
return false;
|
|
}
|
|
|
|
ulong tileRef = mmap.loadedTileRefs.LookupByKey(packedGridPos);
|
|
|
|
// unload, and mark as non loaded
|
|
Detour.dtRawTileData data;
|
|
if (Detour.dtStatusFailed(mmap.navMesh.removeTile(tileRef, out data)))
|
|
{
|
|
// this is technically a memory leak
|
|
// if the grid is later reloaded, dtNavMesh.addTile will return error but no extra memory is used
|
|
// we cannot recover from this error - assert out
|
|
Log.outError(LogFilter.Maps, "MMAP:unloadMap: Could not unload {0:D4}{1:D2}{2:D2}.mmtile from navmesh", mapId, x, y);
|
|
Contract.Assert(false);
|
|
}
|
|
else
|
|
{
|
|
mmap.loadedTileRefs.Remove(packedGridPos);
|
|
--loadedTiles;
|
|
Log.outInfo(LogFilter.Maps, "MMAP:unloadMap: Unloaded mmtile {0:D4}[{1:D2}, {2:D2}] from {3:D4}", mapId, x, y, mapId);
|
|
|
|
var phasedMaps = phaseMapData.LookupByKey(mapId);
|
|
if (!phasedMaps.Empty())
|
|
{
|
|
mmap.DeleteBaseTile(packedGridPos);
|
|
UnloadPhaseTile(phasedMaps, (int)x, (int)y);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool unloadMap(uint mapId)
|
|
{
|
|
if (!loadedMMaps.ContainsKey(mapId))
|
|
{
|
|
// file may not exist, therefore not loaded
|
|
Log.outDebug(LogFilter.Maps, "MMAP:unloadMap: Asked to unload not loaded navmesh map {0:D4}", mapId);
|
|
return false;
|
|
}
|
|
|
|
// unload all tiles from given map
|
|
MMapData mmap = loadedMMaps.LookupByKey(mapId);
|
|
foreach (var i in mmap.loadedTileRefs)
|
|
{
|
|
uint x = (i.Key >> 16);
|
|
uint y = (i.Key & 0x0000FFFF);
|
|
Detour.dtRawTileData data;
|
|
if (Detour.dtStatusFailed(mmap.navMesh.removeTile(i.Value, out data)))
|
|
Log.outError(LogFilter.Maps, "MMAP:unloadMap: Could not unload {0:D4}{1:D2}{2:D2}.mmtile from navmesh", mapId, x, y);
|
|
else
|
|
{
|
|
var phasedMaps = phaseMapData.LookupByKey(mapId);
|
|
if (!phasedMaps.Empty())
|
|
{
|
|
mmap.DeleteBaseTile(i.Key);
|
|
UnloadPhaseTile(phasedMaps, (int)x, (int)y);
|
|
}
|
|
--loadedTiles;
|
|
Log.outInfo(LogFilter.Maps, "MMAP:unloadMap: Unloaded mmtile {0:D4} [{1:D2}, {2:D2}] from {3:D4}", mapId, x, y, mapId);
|
|
}
|
|
}
|
|
|
|
loadedMMaps.Remove(mapId);
|
|
Log.outInfo(LogFilter.Maps, "MMAP:unloadMap: Unloaded {0:D4}.mmap", mapId);
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool unloadMapInstance(uint mapId, uint instanceId)
|
|
{
|
|
// check if we have this map loaded
|
|
MMapData mmap = GetMMapData(mapId);
|
|
if (mmap == null)
|
|
{
|
|
// file may not exist, therefore not loaded
|
|
Log.outDebug(LogFilter.Maps, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map {0}", mapId);
|
|
return false;
|
|
}
|
|
|
|
if (!mmap.navMeshQueries.ContainsKey(instanceId))
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId {0} instanceId {1}", mapId, instanceId);
|
|
return false;
|
|
}
|
|
|
|
mmap.navMeshQueries.Remove(instanceId);
|
|
Log.outInfo(LogFilter.Maps, "MMAP:unloadMapInstance: Unloaded mapId {0} instanceId {1}", mapId, instanceId);
|
|
|
|
return true;
|
|
}
|
|
|
|
public Detour.dtNavMesh GetNavMesh(uint mapId, List<uint> swaps)
|
|
{
|
|
MMapData mmap = GetMMapData(mapId);
|
|
if (mmap == null)
|
|
return null;
|
|
|
|
return mmap.GetNavMesh(swaps);
|
|
}
|
|
|
|
public Detour.dtNavMeshQuery GetNavMeshQuery(uint mapId, uint instanceId, List<uint> swaps)
|
|
{
|
|
MMapData mmap = GetMMapData(mapId);
|
|
if (mmap == null)
|
|
return null;
|
|
|
|
if (!mmap.navMeshQueries.ContainsKey(instanceId))
|
|
{
|
|
// allocate mesh query
|
|
Detour.dtNavMeshQuery query = new Detour.dtNavMeshQuery();
|
|
if (Detour.dtStatusFailed(query.init(mmap.GetNavMesh(swaps), 1024)))
|
|
{
|
|
Log.outError(LogFilter.Maps, "MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId {0} instanceId {1}", mapId, instanceId);
|
|
return null;
|
|
}
|
|
|
|
Log.outInfo(LogFilter.Maps, "MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId {0} instanceId {1}", mapId, instanceId);
|
|
mmap.navMeshQueries.Add(instanceId, query);
|
|
}
|
|
|
|
return mmap.navMeshQueries[instanceId];
|
|
}
|
|
|
|
public uint getLoadedTilesCount() { return loadedTiles; }
|
|
public int getLoadedMapsCount() { return loadedMMaps.Count; }
|
|
|
|
public Dictionary<uint, PhasedTile> GetPhaseTileContainer(uint mapId) { return _phaseTiles.LookupByKey(mapId); }
|
|
|
|
Dictionary<uint, MMapData> loadedMMaps = new Dictionary<uint, MMapData>();
|
|
MultiMap<uint, uint> phaseMapData = new MultiMap<uint, uint>();
|
|
Dictionary<uint, Dictionary<uint, PhasedTile>> _phaseTiles = new Dictionary<uint, Dictionary<uint, PhasedTile>>();
|
|
uint loadedTiles;
|
|
}
|
|
|
|
public class MMapData
|
|
{
|
|
public MMapData(Detour.dtNavMesh mesh, uint mapId)
|
|
{
|
|
navMesh = mesh;
|
|
_mapId = mapId;
|
|
}
|
|
|
|
void RemoveSwap(PhasedTile ptile, uint swap, uint packedXY)
|
|
{
|
|
uint x = (packedXY >> 16);
|
|
uint y = (packedXY & 0x0000FFFF);
|
|
|
|
if (!loadedPhasedTiles[swap].Contains(packedXY))
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: mmtile {0:D4}[{1:D2}, {2:D2}] unload skipped, due to not loaded", swap, x, y);
|
|
return;
|
|
}
|
|
Detour.dtMeshHeader header = ptile.data.header;
|
|
|
|
Detour.dtRawTileData data;
|
|
// remove old tile
|
|
if (Detour.dtStatusFailed(navMesh.removeTile(loadedTileRefs[packedXY], out data)))
|
|
Log.outError(LogFilter.Maps, "MMapData.RemoveSwap: Could not unload phased {0:D4}{1:D2}{2:D2}.mmtile from navmesh", swap, x, y);
|
|
else
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: Unloaded phased {0:D4}{1:D2}{2:D2}.mmtile from navmesh", swap, x, y);
|
|
|
|
// restore base tile
|
|
ulong loadedRef = 0;
|
|
if (Detour.dtStatusSucceed(navMesh.addTile(_baseTiles[packedXY].data, 0, 0, ref loadedRef)))
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: Loaded base mmtile {0:D4}[{1:D2}, {2:D2}] into {0:D4}[{1:D2}, {2:D2}]", _mapId, x, y, _mapId, header.x, header.y);
|
|
}
|
|
else
|
|
Log.outError(LogFilter.Maps, "MMapData.RemoveSwap: Could not load base {0:D4}{1:D2}{2:D2}.mmtile to navmesh", _mapId, x, y);
|
|
|
|
loadedTileRefs[packedXY] = loadedRef;
|
|
}
|
|
|
|
loadedPhasedTiles.Remove(swap, packedXY);
|
|
|
|
if (loadedPhasedTiles[swap].Empty())
|
|
{
|
|
_activeSwaps.Remove(swap);
|
|
Log.outDebug(LogFilter.Maps, "MMapData.RemoveSwap: Fully removed swap {0} from map {1}", swap, _mapId);
|
|
}
|
|
}
|
|
|
|
void AddSwap(PhasedTile ptile, uint swap, uint packedXY)
|
|
{
|
|
uint x = (packedXY >> 16);
|
|
uint y = (packedXY & 0x0000FFFF);
|
|
|
|
if (!loadedTileRefs.ContainsKey(packedXY))
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: phased mmtile {0:D4}[{1:D2}, {2:D2}] load skipped, due to not loaded base tile on map {3}", swap, x, y, _mapId);
|
|
return;
|
|
}
|
|
if (loadedPhasedTiles[swap].Contains(packedXY))
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: WARNING! phased mmtile {0:D4}[{1:D2}, {2:D2}] load skipped, due to already loaded on map {3}", swap, x, y, _mapId);
|
|
return;
|
|
}
|
|
|
|
Detour.dtMeshHeader header = ptile.data.header;
|
|
|
|
Detour.dtMeshTile oldTile = navMesh.getTileByRef(loadedTileRefs[packedXY]);
|
|
if (oldTile == null)
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: phased mmtile {0:D4}[{1:D2}, {2:D2}] load skipped, due to not loaded base tile ref on map {3}", swap, x, y, _mapId);
|
|
return;
|
|
}
|
|
|
|
// header xy is based on the swap map's tile set, wich doesn't have all the same tiles as root map, so copy the xy from the orignal header
|
|
header.x = oldTile.header.x;
|
|
header.y = oldTile.header.y;
|
|
|
|
Detour.dtRawTileData data;
|
|
// remove old tile
|
|
if (Detour.dtStatusFailed(navMesh.removeTile(loadedTileRefs[packedXY], out data)))
|
|
Log.outError(LogFilter.Maps, "MMapData.AddSwap: Could not unload {0:D4}{1:D2}{2:D2}.mmtile from navmesh", _mapId, x, y);
|
|
else
|
|
{
|
|
Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: Unloaded {0:D4}{1:D2}{2:D2}.mmtile from navmesh", _mapId, x, y);
|
|
|
|
_activeSwaps.Add(swap);
|
|
loadedPhasedTiles.Add(swap, packedXY);
|
|
|
|
// add new swapped tile
|
|
ulong loadedRef = 0;
|
|
if (Detour.dtStatusSucceed(navMesh.addTile(ptile.data, 0, 0, ref loadedRef)))
|
|
Log.outDebug(LogFilter.Maps, "MMapData.AddSwap: Loaded phased mmtile {0:D4}[{1:D2}, {2:D2}] into {0:D4}[{1:D2}, {2:D2}]", swap, x, y, _mapId, header.x, header.y);
|
|
else
|
|
Log.outError(LogFilter.Maps, "MMapData.AddSwap: Could not load {0:D4}{1:D2}{2:D2}.mmtile to navmesh", swap, x, y);
|
|
|
|
loadedTileRefs[packedXY] = loadedRef;
|
|
}
|
|
}
|
|
|
|
public Detour.dtNavMesh GetNavMesh(List<uint> swaps)
|
|
{
|
|
foreach (uint swap in _activeSwaps)
|
|
{
|
|
if (!swaps.Contains(swap)) // swap not active
|
|
{
|
|
var ptc = Global.MMapMgr.GetPhaseTileContainer(swap);
|
|
foreach (var pair in ptc)
|
|
RemoveSwap(pair.Value, swap, pair.Key); // remove swap
|
|
}
|
|
}
|
|
|
|
// for each of the calling unit's terrain swaps
|
|
foreach (uint swap in swaps)
|
|
{
|
|
if (!_activeSwaps.Contains(swap)) // swap not active
|
|
{
|
|
// for each of the terrain swap's xy tiles
|
|
var ptc = Global.MMapMgr.GetPhaseTileContainer(swap);
|
|
if (ptc != null)
|
|
{
|
|
foreach (var pair in ptc)
|
|
AddSwap(pair.Value, swap, pair.Key); // add swap
|
|
}
|
|
}
|
|
}
|
|
|
|
return navMesh;
|
|
}
|
|
|
|
public void AddBaseTile(uint packedGridPos, Detour.dtRawTileData data, MmapTileHeader fileHeader, uint dataSize)
|
|
{
|
|
if (!_baseTiles.ContainsKey(packedGridPos))
|
|
{
|
|
PhasedTile phasedTile = new PhasedTile();
|
|
phasedTile.data = data;
|
|
phasedTile.fileHeader = fileHeader;
|
|
phasedTile.dataSize = (int)dataSize;
|
|
_baseTiles[packedGridPos] = phasedTile;
|
|
}
|
|
}
|
|
|
|
public void DeleteBaseTile(uint packedGridPos)
|
|
{
|
|
var phaseTile = _baseTiles.LookupByKey(packedGridPos);
|
|
if (phaseTile != null)
|
|
{
|
|
_baseTiles.Remove(packedGridPos);
|
|
}
|
|
}
|
|
|
|
public Dictionary<uint, Detour.dtNavMeshQuery> navMeshQueries = new Dictionary<uint, Detour.dtNavMeshQuery>(); // instanceId to query
|
|
|
|
public Detour.dtNavMesh navMesh;
|
|
public Dictionary<uint, ulong> loadedTileRefs = new Dictionary<uint, ulong>();
|
|
MultiMap<uint, uint> loadedPhasedTiles = new MultiMap<uint, uint>();
|
|
|
|
uint _mapId;
|
|
Dictionary<uint, PhasedTile> _baseTiles = new Dictionary<uint, PhasedTile>();
|
|
List<uint> _activeSwaps = new List<uint>();
|
|
}
|
|
|
|
public class PhasedTile
|
|
{
|
|
public Detour.dtRawTileData data;
|
|
public MmapTileHeader fileHeader;
|
|
public int dataSize;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct MmapTileHeader
|
|
{
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
|
|
public char[] mmapMagic;
|
|
public uint dtVersion;
|
|
public uint mmapVersion;
|
|
public uint size;
|
|
public byte usesLiquids;
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
|
|
public byte[] padding;
|
|
}
|
|
}
|