using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Navigation;
/// <summary>Fuel presentation of the original game route; never creates or changes its waypoints.</summary>
public sealed record NavigationFuelOverlay(IReadOnlyList<long> DashedLegs,IReadOnlyList<long> SupplySites,long? RecommendedSupply)
{
public static readonly NavigationFuelOverlay Empty=new([],[],null);
public static NavigationFuelOverlay Build(PlayerState player,double reserve)
{
var route=player.Navigation.Route;
var current=route.ToList().FindIndex(w=>w.SystemAddress==player.Navigation.CurrentSystemAddress);
var ship=player.Ships.FirstOrDefault(s=>s.ShipId==player.CurrentShipId);
var drive=NavigationPreviewBuilder.Drive(ship);
var main=player.Navigation.FuelAt>player.StatusAt?player.Navigation.MainFuel:player.Status?.FuelMain??player.Navigation.MainFuel;
if(current<0||drive is null||main is not {} fuel||!double.IsFinite(fuel))return Empty;
var sites=NavigationPreviewBuilder.HasScoop(ship)?route.Skip(current).Where(w=>w.Scoopable).Select(w=>w.SystemAddress).ToArray():[];
double? mass=ship?.UnladenMass is {} dry&&player.Status?.FuelReserve is {} aux&&player.CargoReliable?dry+fuel+aux+player.CargoCount:null;
var dashed=new List<long>();long? lastReachableSupply=sites.Contains(route[current].SystemAddress)?route[current].SystemAddress:null,recommended=null;
var outsideRange=false;
for(var i=current+1;i<route.Count;i++)
{
var cost=drive.MaxFuelPerJump;
if(mass is {} total)
{
try{cost=NavigationPreviewBuilder.EstimateFuel(drive,total,route[i-1].Position.DistanceTo(route[i].Position));}
catch(InvalidOperationException){mass=null;}
}
fuel-=cost;if(mass is {} before)mass=before-cost;
if(fuel<reserve)
{
if(!outsideRange)recommended=lastReachableSupply;
outsideRange=true;
}
if(outsideRange)dashed.Add(route[i].SystemAddress);
else if(sites.Contains(route[i].SystemAddress))lastReachableSupply=route[i].SystemAddress;
}
return new(dashed,sites,recommended);
}
}
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Navigation;
/// <summary>Fuel presentation of the original game route; never creates or changes its waypoints.</summary>
public sealed record NavigationFuelOverlay(IReadOnlyList<long> DashedLegs,IReadOnlyList<long> SupplySites,long? RecommendedSupply)
{
public static readonly NavigationFuelOverlay Empty=new([],[],null);
public static NavigationFuelOverlay Build(PlayerState player,double reserve)
{
var route=player.Navigation.Route;
var current=route.ToList().FindIndex(w=>w.SystemAddress==player.Navigation.CurrentSystemAddress);
var ship=player.Ships.FirstOrDefault(s=>s.ShipId==player.CurrentShipId);
var drive=NavigationPreviewBuilder.Drive(ship);
var main=player.Navigation.FuelAt>player.StatusAt?player.Navigation.MainFuel:player.Status?.FuelMain??player.Navigation.MainFuel;
if(current<0||drive is null||main is not {} fuel||!double.IsFinite(fuel))return Empty;
var sites=NavigationPreviewBuilder.HasScoop(ship)?route.Skip(current).Where(w=>w.Scoopable).Select(w=>w.SystemAddress).ToArray():[];
double? mass=ship?.UnladenMass is {} dry&&player.Status?.FuelReserve is {} aux&&player.CargoReliable?dry+fuel+aux+player.CargoCount:null;
var dashed=new List<long>();long? lastReachableSupply=sites.Contains(route[current].SystemAddress)?route[current].SystemAddress:null,recommended=null;
var outsideRange=false;
for(var i=current+1;i<route.Count;i++)
{
var cost=drive.MaxFuelPerJump;
if(mass is {} total)
{
try{cost=NavigationPreviewBuilder.EstimateFuel(drive,total,route[i-1].Position.DistanceTo(route[i].Position));}
catch(InvalidOperationException){mass=null;}
}
fuel-=cost;if(mass is {} before)mass=before-cost;
if(fuel<reserve)
{
if(!outsideRange)recommended=lastReachableSupply;
outsideRange=true;
}
if(outsideRange)dashed.Add(route[i].SystemAddress);
else if(sites.Contains(route[i].SystemAddress))lastReachableSupply=route[i].SystemAddress;
}
return new(dashed,sites,recommended);
}
}