// // Path.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. namespace NosSmooth.Extensions.Pathfinding; /// /// Represents a found walkable path. /// public class Path { /// /// Initializes a new instance of the class. /// /// The map id. /// The current x. /// The current y. /// The target x. /// The target y. /// The parts that represent the path from the current to the target. public Path ( int map, short x, short y, short targetX, short targetY, IReadOnlyList<(short X, short Y)> parts ) { MapId = map; CurrentX = x; CurrentY = y; TargetX = targetX; TargetY = targetY; Parts = parts; CurrentPartIndex = 0; } /// /// Gets the map id this path is for. /// public int MapId { get; } /// /// Gets whether the path has reached an end. /// public bool ReachedEnd => CurrentPartIndex >= Parts.Count - 1; /// /// Gets the current walk path index. /// public int CurrentPartIndex { get; private set; } /// /// Gets the list of the parts that have to be taken. /// public IReadOnlyList<(short X, short Y)> Parts { get; } /// /// Gets the target x coordinate. /// public short TargetX { get; } /// /// Gets the target y coordinate. /// public short TargetY { get; } /// /// Gets the current x coordinate. /// public short CurrentX { get; private set; } /// /// gets the current y coordinate. /// public short CurrentY { get; private set; } /// /// Take a path only in the same direction. /// /// A position to walk to. public (short X, short Y) TakeForwardPath() { if (ReachedEnd || CurrentPartIndex + 2 >= Parts.Count) { return (TargetX, TargetY); } var zeroTile = (CurrentX, CurrentY); var firstTile = Parts[++CurrentPartIndex]; var currentTile = firstTile; var nextTile = Parts[CurrentPartIndex + 1]; while (!ReachedEnd && IsInLine(zeroTile, firstTile, nextTile)) { currentTile = nextTile; CurrentPartIndex++; if (!ReachedEnd) { nextTile = Parts[CurrentPartIndex + 1]; } } return currentTile; } private bool IsInLine((short X, short Y) start, (short X, short Y) first, (short X, short Y) current) { var xFirstDiff = first.X - start.X; var yFirstDiff = first.Y - start.Y; var xCurrentDiff = current.X - start.X; var yCurrentDiff = current.Y - start.Y; if (xFirstDiff == 0 && yFirstDiff == 0) { throw new ArgumentException("The path went back to the start."); } if (xCurrentDiff == 0 && yCurrentDiff == 0) { throw new ArgumentException("The path went back to the start."); } if (xFirstDiff != 0) { var xRatio = xCurrentDiff / (float)xFirstDiff; return (yFirstDiff * xRatio) - yCurrentDiff < float.Epsilon * 10; } var yRatio = yCurrentDiff / (float)yFirstDiff; return (xFirstDiff * yRatio) - xCurrentDiff < float.Epsilon * 10; } /// /// Take the given number of tiles and return the position we ended up at. /// /// /// If the count is greater than what is remaining, the end will be taken. /// /// The count of parts to take. /// A position to walk to. public (short X, short Y) TakePath(uint partsCount) { if (ReachedEnd) { return (TargetX, TargetY); } if (CurrentPartIndex + partsCount >= Parts.Count - 1) { CurrentPartIndex = Parts.Count - 1; } else { CurrentPartIndex += (int)partsCount; } return Parts[CurrentPartIndex]; } }