ulong GetPolyByLocation(float[] point, ref float distance) { // first we check the current path // if the current path doesn't contain the current poly, // we need to use the expensive navMesh.findNearestPoly ulong polyRef = GetPathPolyByPosition(_pathPolyRefs, _polyLength, point, ref distance); if (polyRef != 0) { return(polyRef); } // we don't have it in our old path // try to get it by findNearestPoly() // first try with low search box float[] extents = { 3.0f, 5.0f, 3.0f }; // bounds of poly search area float[] closestPoint = { 0.0f, 0.0f, 0.0f }; if (Detour.dtStatusSucceed(_navMeshQuery.findNearestPoly(point, extents, _filter, ref polyRef, ref closestPoint)) && polyRef != 0) { distance = Detour.dtVdist(closestPoint, point); return(polyRef); } // still nothing .. // try with bigger search box // Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery.findNearestPoly) extents[1] = 50.0f; if (Detour.dtStatusSucceed(_navMeshQuery.findNearestPoly(point, extents, _filter, ref polyRef, ref closestPoint)) && polyRef != 0) { distance = Detour.dtVdist(closestPoint, point); return(polyRef); } return(0); }
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; } }
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); } }
uint FindSmoothPath(float[] startPos, float[] endPos, ulong[] polyPath, uint polyPathSize, out float[] smoothPath, out int smoothPathSize, uint maxSmoothPathSize) { smoothPathSize = 0; int nsmoothPath = 0; smoothPath = new float[74 * 3]; ulong[] polys = new ulong[74]; Array.Copy(polyPath, polys, polyPathSize); uint npolys = polyPathSize; float[] iterPos = new float[3]; float[] targetPos = new float[3]; if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPolyBoundary(polys[0], startPos, iterPos))) { return(Detour.DT_FAILURE); } if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPolyBoundary(polys[npolys - 1], endPos, targetPos))) { return(Detour.DT_FAILURE); } Detour.dtVcopy(smoothPath, nsmoothPath * 3, iterPos, 0); nsmoothPath++; // Move towards target a small advancement at a time until target reached or // when ran out of memory to store the path. while (npolys != 0 && nsmoothPath < maxSmoothPathSize) { // Find location to steer towards. float[] steerPos; Detour.dtStraightPathFlags steerPosFlag; ulong steerPosRef = 0; if (!GetSteerTarget(iterPos, targetPos, 0.3f, polys, npolys, out steerPos, out steerPosFlag, out steerPosRef)) { break; } bool endOfPath = steerPosFlag.HasAnyFlag(Detour.dtStraightPathFlags.DT_STRAIGHTPATH_END); bool offMeshConnection = steerPosFlag.HasAnyFlag(Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION); // Find movement delta. float[] delta = new float[3]; Detour.dtVsub(delta, steerPos, iterPos); float len = (float)Math.Sqrt(Detour.dtVdot(delta, delta)); // If the steer target is end of path or off-mesh link, do not move past the location. if ((endOfPath || offMeshConnection) && len < 4.0f) { len = 1.0f; } else { len = 4.0f / len; } float[] moveTgt = new float[3]; Detour.dtVmad(moveTgt, iterPos, delta, len); // Move float[] result = new float[3]; int MAX_VISIT_POLY = 16; ulong[] visited = new ulong[MAX_VISIT_POLY]; int nvisited = 0; _navMeshQuery.moveAlongSurface(polys[0], iterPos, moveTgt, _filter, result, visited, ref nvisited, MAX_VISIT_POLY); npolys = FixupCorridor(polys, npolys, 74, visited, nvisited); _navMeshQuery.getPolyHeight(polys[0], result, ref result[1]); result[1] += 0.5f; Detour.dtVcopy(iterPos, result); // Handle end of path and off-mesh links when close enough. if (endOfPath && InRangeYZX(iterPos, steerPos, 0.3f, 1.0f)) { // Reached end of path. Detour.dtVcopy(iterPos, targetPos); if (nsmoothPath < maxSmoothPathSize) { Detour.dtVcopy(smoothPath, nsmoothPath * 3, iterPos, 0); nsmoothPath++; } break; } else if (offMeshConnection && InRangeYZX(iterPos, steerPos, 0.3f, 1.0f)) { // Advance the path up to and over the off-mesh connection. ulong prevRef = 0; ulong polyRef = polys[0]; uint npos = 0; while (npos < npolys && polyRef != steerPosRef) { prevRef = polyRef; polyRef = polys[npos]; npos++; } for (uint i = npos; i < npolys; ++i) { polys[i - npos] = polys[i]; } npolys -= npos; // Handle the connection. float[] connectionStartPos = new float[3]; float[] connectionEndPos = new float[3]; if (Detour.dtStatusSucceed(_navMesh.getOffMeshConnectionPolyEndPoints(prevRef, polyRef, connectionStartPos, connectionEndPos))) { if (nsmoothPath < maxSmoothPathSize) { Detour.dtVcopy(smoothPath, nsmoothPath * 3, connectionStartPos, 0); nsmoothPath++; } // Move position at the other side of the off-mesh link. Detour.dtVcopy(iterPos, connectionEndPos); _navMeshQuery.getPolyHeight(polys[0], iterPos, ref iterPos[1]); iterPos[1] += 0.5f; } } // Store results. if (nsmoothPath < maxSmoothPathSize) { Detour.dtVcopy(smoothPath, nsmoothPath * 3, iterPos, 0); nsmoothPath++; } } smoothPathSize = nsmoothPath; // this is most likely a loop return(nsmoothPath < 74 ? Detour.DT_SUCCESS : Detour.DT_FAILURE); }
void BuildPolyPath(Vector3 startPos, Vector3 endPos) { // *** getting start/end poly logic *** float distToStartPoly = 0; float distToEndPoly = 0; float[] startPoint = { startPos.Y, startPos.Z, startPos.X }; float[] endPoint = { endPos.Y, endPos.Z, endPos.X }; ulong startPoly = GetPolyByLocation(startPoint, ref distToStartPoly); ulong endPoly = GetPolyByLocation(endPoint, ref distToEndPoly); // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying and swimming exception ) // its up to caller how he will use this info if (startPoly == 0 || endPoly == 0) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); bool path = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanFly(); bool waterPath = _sourceUnit.IsTypeId(TypeId.Unit) && _sourceUnit.ToCreature().CanSwim(); if (waterPath) { // Check both start and end points, if they're both in water, then we can *safely* let the creature move for (uint i = 0; i < _pathPoints.Length; ++i) { ZLiquidStatus status = _sourceUnit.GetMap().getLiquidStatus(_pathPoints[i].X, _pathPoints[i].Y, _pathPoints[i].Z, MapConst.MapAllLiquidTypes); // One of the points is not in the water, cancel movement. if (status == ZLiquidStatus.NoWater) { waterPath = false; break; } } } pathType = (path || waterPath) ? (PathType.Normal | PathType.NotUsingPath) : PathType.NoPath; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . farFromPoly distToStartPoly={0:F3} distToEndPoly={1:F3}\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (_sourceUnit.IsTypeId(TypeId.Unit)) { Creature owner = _sourceUnit.ToCreature(); Vector3 p = (distToStartPoly > 7.0f) ? startPos : endPos; if (_sourceUnit.GetMap().IsUnderWater(p.X, p.Y, p.Z)) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . underWater case\n"); if (owner.CanSwim()) { buildShotrcut = true; } } else { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . flying case\n"); if (owner.CanFly()) { buildShotrcut = true; } } } if (buildShotrcut) { BuildShortcut(); pathType = (PathType.Normal | PathType.NotUsingPath); return; } else { float[] closestPoint = new float[3]; // we may want to use closestPointOnPolyBoundary instead bool posOverPoly = false; if (Detour.dtStatusSucceed(_navMeshQuery.closestPointOnPoly(endPoly, endPoint, closestPoint, ref posOverPoly))) { Detour.dtVcopy(endPoint, closestPoint); SetActualEndPosition(new Vector3(endPoint[2], endPoint[0], endPoint[1])); } pathType = PathType.Incomplete; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (startPoly == endPoly)\n"); BuildShortcut(); _pathPolyRefs[0] = startPoly; _polyLength = 1; pathType = farFromPoly ? PathType.Incomplete : PathType.Normal; Log.outDebug(LogFilter.Maps, "BuildPolyPath . path type {0}\n", pathType); return; } // look for startPoly/endPoly in current path /// @todo we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint pathStartIndex = 0; uint pathEndIndex = 0; if (_polyLength != 0) { for (; pathStartIndex < _polyLength; ++pathStartIndex) { // here to carch few bugs if (_pathPolyRefs[pathStartIndex] == 0) { Log.outError(LogFilter.Maps, "Invalid poly ref in BuildPolyPath. _polyLength: {0}, pathStartIndex: {1}," + " startPos: {2}, endPos: {3}, mapid: {4}", _polyLength, pathStartIndex, startPos, endPos, _sourceUnit.GetMapId()); break; } if (_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } } for (pathEndIndex = _polyLength - 1; pathEndIndex > pathStartIndex; --pathEndIndex) { if (_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } } if (startPolyFound && endPolyFound) { Log.outDebug(LogFilter.Maps, "BuildPolyPath : (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out _polyLength = pathEndIndex - pathStartIndex + 1; Array.Copy(_pathPolyRefs, pathStartIndex, _pathPolyRefs, 0, _polyLength); } else if (startPolyFound && !endPolyFound) { Log.outDebug(LogFilter.Maps, "BuildPolyPath : (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready _polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length /// @todo play with the values here uint prefixPolyLength = (uint)(_polyLength * 0.8f + 0.5f); Array.Copy(_pathPolyRefs, pathStartIndex, _pathPolyRefs, 0, prefixPolyLength); ulong suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float[] suffixEndPoint = new float[3]; bool posOverPoly = false; if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, ref posOverPoly))) { // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that // try to recover by using prev polyref --prefixPolyLength; suffixStartPoly = _pathPolyRefs[prefixPolyLength - 1]; if (Detour.dtStatusFailed(_navMeshQuery.closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, ref posOverPoly))) { // suffixStartPoly is still invalid, error state BuildShortcut(); pathType = PathType.NoPath; return; } } // generate suffix uint suffixPolyLength = 0; uint dtResult; if (_straightLine) { float hit = 0; float[] hitNormal = new float[3]; dtResult = _navMeshQuery.raycast( suffixStartPoly, suffixEndPoint, endPoint, _filter, ref hit, hitNormal, _pathPolyRefs, ref suffixPolyLength, 74 - (int)prefixPolyLength); // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != float.MaxValue) { // the ray hit something, return no path instead of the incomplete one pathType = PathType.NoPath; return; } } else { dtResult = _navMeshQuery.findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position _filter, // polygon search filter _pathPolyRefs, ref suffixPolyLength, 74 - (int)prefixPolyLength); } if (suffixPolyLength == 0 || Detour.dtStatusFailed(dtResult)) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString()); } Log.outDebug(LogFilter.Maps, "m_polyLength={0} prefixPolyLength={1} suffixPolyLength={2} \n", _polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap _polyLength = prefixPolyLength + suffixPolyLength - 1; } else { Log.outDebug(LogFilter.Maps, "++ BuildPolyPath . (!startPolyFound && !endPolyFound)\n"); // either we have no path at all . first run // or something went really wrong . we aren't moving along the path to the target // just generate new path // free and invalidate old path data Clear(); uint dtResult; if (_straightLine) { float hit = 0; float[] hitNormal = new float[3]; dtResult = _navMeshQuery.raycast( startPoly, startPoint, endPoint, _filter, ref hit, hitNormal, _pathPolyRefs, ref _polyLength, 74); // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != float.MaxValue) { // the ray hit something, return no path instead of the incomplete one pathType = PathType.NoPath; return; } } else { dtResult = _navMeshQuery.findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position _filter, // polygon search filter _pathPolyRefs, // [out] path ref _polyLength, 74); // max number of polygons in output path } if (_polyLength == 0 || Detour.dtStatusFailed(dtResult)) { // only happens if we passed bad data to findPath(), or navmesh is messed up Log.outError(LogFilter.Maps, "{0}'s Path Build failed: 0 length path", _sourceUnit.GetGUID().ToString()); BuildShortcut(); pathType = PathType.NoPath; return; } } // by now we know what type of path we can get if (_pathPolyRefs[_polyLength - 1] == endPoly && !pathType.HasAnyFlag(PathType.Incomplete)) { pathType = PathType.Normal; } else { pathType = PathType.Incomplete; } // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }
public static SmoothPath ComputeSmoothPath(Detour.dtNavMeshQuery navQuery, float[] startWorldPos, float[] endWorldPos, float distance = 10) { SmoothPath smoothPath = new SmoothPath(); if (navQuery == null) { return(smoothPath); } float[] extents = new float[3]; for (int i = 0; i < 3; ++i) { extents[i] = distance; } dtPolyRef startRef = 0; dtPolyRef endRef = 0; float[] startPt = new float[3]; float[] endPt = new float[3]; Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); navQuery.findNearestPoly(startWorldPos, extents, filter, ref startRef, ref startPt); navQuery.findNearestPoly(endWorldPos, extents, filter, ref endRef, ref endPt); const int maxPath = SmoothPath.MAX_POLYS; dtPolyRef[] path = new dtPolyRef[maxPath]; int pathCount = -1; navQuery.findPath(startRef, endRef, startPt, endPt, filter, path, ref pathCount, maxPath); smoothPath.m_nsmoothPath = 0; if (pathCount > 0) { // Iterate over the path to find smooth path on the detail mesh surface. dtPolyRef[] polys = new dtPolyRef[SmoothPath.MAX_POLYS]; for (int i = 0; i < pathCount; ++i) { polys[i] = path[i]; } int npolys = pathCount; float[] iterPos = new float[3]; float[] targetPos = new float[3]; bool posOverPoly_dummy = false; navQuery.closestPointOnPoly(startRef, startPt, iterPos, ref posOverPoly_dummy); navQuery.closestPointOnPoly(polys[npolys - 1], endPt, targetPos, ref posOverPoly_dummy); const float STEP_SIZE = 0.5f; const float SLOP = 0.01f; smoothPath.m_nsmoothPath = 0; Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, iterPos, 0); smoothPath.m_nsmoothPath++; // Move towards target a small advancement at a time until target reached or // when ran out of memory to store the path. while (npolys != 0 && smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { // Find location to steer towards. float[] steerPos = new float[3]; byte steerPosFlag = 0; dtPolyRef steerPosRef = 0; if (!getSteerTarget(navQuery, iterPos, targetPos, SLOP, polys, npolys, steerPos, ref steerPosFlag, ref steerPosRef)) { break; } bool endOfPath = (steerPosFlag & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_END) != 0 ? true : false; bool offMeshConnection = (steerPosFlag & (byte)Detour.dtStraightPathFlags.DT_STRAIGHTPATH_OFFMESH_CONNECTION) != 0 ? true : false; // Find movement delta. float[] delta = new float[3]; //, len; float len = .0f; Detour.dtVsub(delta, steerPos, iterPos); len = (float)Mathf.Sqrt(Detour.dtVdot(delta, delta)); // If the steer target is end of path or off-mesh link, do not move past the location. if ((endOfPath || offMeshConnection) && len < STEP_SIZE) { len = 1; } else { len = STEP_SIZE / len; } float[] moveTgt = new float[3]; Detour.dtVmad(moveTgt, iterPos, delta, len); // Move float[] result = new float[3]; dtPolyRef[] visited = new dtPolyRef[16]; int nvisited = 0; navQuery.moveAlongSurface(polys[0], iterPos, moveTgt, filter, result, visited, ref nvisited, 16); npolys = fixupCorridor(polys, npolys, SmoothPath.MAX_POLYS, visited, nvisited); npolys = fixupShortcuts(polys, npolys, navQuery); float h = 0; dtStatus getHeightStatus = navQuery.getPolyHeight(polys[0], result, ref h); result[1] = h; if ((getHeightStatus & Detour.DT_FAILURE) != 0) { Debug.LogError("Failed to getPolyHeight " + polys[0] + " pos " + result[0] + " " + result[1] + " " + result[2] + " h " + h); } Detour.dtVcopy(iterPos, result); // Handle end of path and off-mesh links when close enough. if (endOfPath && inRange(iterPos, 0, steerPos, 0, SLOP, 1.0f)) { // Reached end of path. Detour.dtVcopy(iterPos, targetPos); if (smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, iterPos, 0); smoothPath.m_nsmoothPath++; } break; } else if (offMeshConnection && inRange(iterPos, 0, steerPos, 0, SLOP, 1.0f)) { // Reached off-mesh connection. float[] startPos = new float[3]; //, endPos[3]; float[] endPos = new float[3]; // Advance the path up to and over the off-mesh connection. dtPolyRef prevRef = 0, polyRef = polys[0]; int npos = 0; while (npos < npolys && polyRef != steerPosRef) { prevRef = polyRef; polyRef = polys[npos]; npos++; } for (int i = npos; i < npolys; ++i) { polys[i - npos] = polys[i]; } npolys -= npos; // Handle the connection. dtStatus status = navQuery.getAttachedNavMesh().getOffMeshConnectionPolyEndPoints(prevRef, polyRef, startPos, endPos); if (Detour.dtStatusSucceed(status)) { if (smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, startPos, 0); smoothPath.m_nsmoothPath++; // Hack to make the dotted path not visible during off-mesh connection. if ((smoothPath.m_nsmoothPath & 1) != 0) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, startPos, 0); smoothPath.m_nsmoothPath++; } } // Move position at the other side of the off-mesh link. Detour.dtVcopy(iterPos, endPos); float eh = 0.0f; navQuery.getPolyHeight(polys[0], iterPos, ref eh); iterPos[1] = eh; } } // Store results. if (smoothPath.m_nsmoothPath < SmoothPath.MAX_SMOOTH) { Detour.dtVcopy(smoothPath.m_smoothPath, smoothPath.m_nsmoothPath * 3, iterPos, 0); smoothPath.m_nsmoothPath++; } } } return(smoothPath); }
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); } }
static bool LocCommand(StringArguments args, CommandHandler handler) { handler.SendSysMessage("mmap tileloc:"); // grid tile location Player player = handler.GetPlayer(); int gx = (int)(32 - player.GetPositionX() / MapConst.SizeofGrids); int gy = (int)(32 - player.GetPositionY() / MapConst.SizeofGrids); float x, y, z; player.GetPosition(out x, out y, out z); handler.SendSysMessage("{0:D4}{1:D2}{2:D2}.mmtile", player.GetMapId(), gy, gx); handler.SendSysMessage("gridloc [{0}, {1}]", gx, gy); // calculate navmesh tile location uint terrainMapId = PhasingHandler.GetTerrainMapId(player.GetPhaseShift(), player.GetMap(), x, y); Detour.dtNavMesh navmesh = Global.MMapMgr.GetNavMesh(terrainMapId); Detour.dtNavMeshQuery navmeshquery = Global.MMapMgr.GetNavMeshQuery(terrainMapId, player.GetInstanceId()); if (navmesh == null || navmeshquery == null) { handler.SendSysMessage("NavMesh not loaded for current map."); return(true); } float[] min = navmesh.getParams().orig; float[] location = { y, z, x }; float[] extents = { 3.0f, 5.0f, 3.0f }; int tilex = (int)((y - min[0]) / MapConst.SizeofGrids); int tiley = (int)((x - min[2]) / MapConst.SizeofGrids); handler.SendSysMessage("Calc [{0:D2}, {1:D2}]", tilex, tiley); // navmesh poly . navmesh tile location Detour.dtQueryFilter filter = new Detour.dtQueryFilter(); float[] nothing = new float[3]; ulong polyRef = 0; if (Detour.dtStatusFailed(navmeshquery.findNearestPoly(location, extents, filter, ref polyRef, ref nothing))) { handler.SendSysMessage("Dt [??,??] (invalid poly, probably no tile loaded)"); return(true); } if (polyRef == 0) { handler.SendSysMessage("Dt [??, ??] (invalid poly, probably no tile loaded)"); } else { Detour.dtMeshTile tile = new Detour.dtMeshTile(); Detour.dtPoly poly = new Detour.dtPoly(); if (Detour.dtStatusSucceed(navmesh.getTileAndPolyByRef(polyRef, ref tile, ref poly))) { if (tile != null) { handler.SendSysMessage("Dt [{0:D2},{1:D2}]", tile.header.x, tile.header.y); return(false); } } handler.SendSysMessage("Dt [??,??] (no tile loaded)"); } return(true); }
bool LoadMapImpl(string basePath, uint mapId, uint x, uint y) { // make sure the mmap is loaded and ready to load tiles if (!LoadMapData(basePath, mapId)) { return(false); } // get this mmap data MMapData mmap = loadedMMaps[mapId]; Cypher.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, basePath, mapId, x, y); if (!File.Exists(fileName)) { if (parentMapData.ContainsKey(mapId)) { fileName = string.Format(TILE_FILE_NAME_FORMAT, basePath, parentMapData[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.Read <MmapTileHeader>(); if (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, 1, 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); 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); } }