//
// 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];
}
}