using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Navigation;
public sealed record FlightFuelBudget(double NextJump,double UntilRefuel,bool UsesMassReference);
public static class NavigationFuelBudget
{
public static FlightFuelBudget Calculate(PlayerState player,ShipRecord ship,FsdReference drive,double fuel,double reserve)
{
var route=player.Navigation.Route;
var current=route.ToList().FindIndex(w=>w.SystemAddress==player.Navigation.CurrentSystemAddress);
var legs=new List<double>();
if(current>=0)for(var i=current+1;i<route.Count;i++)
{
legs.Add(route[i-1].Position.DistanceTo(route[i].Position));
if(route[i].Scoopable&&NavigationPreviewBuilder.HasScoop(ship))break;
}
var maximum=drive.MaxFuelPerJump;
var fallback=new FlightFuelBudget(maximum,maximum*Math.Max(1,legs.Count)+reserve+.25,false);
if(legs.Count==0||!player.CargoReliable||ship.UnladenMass is not >0||player.Status?.FuelReserve is not {} auxiliary||!double.IsFinite(fuel))return fallback;
var dry=ship.UnladenMass.Value+player.CargoCount+auxiliary;
var carried=fuel;double first=maximum,budget=fallback.UntilRefuel;
for(var iteration=0;iteration<12;iteration++)
{
try
{
// Do not subtract earlier burns from mass: later costs stay conservative.
var costs=legs.Select(distance=>Math.Min(maximum,NavigationPreviewBuilder.EstimateFuel(drive,dry+carried,distance)*1.04+.01)).ToArray();
first=costs[0];budget=costs.Sum()+reserve+.25;
if(budget<=carried+.001)return new(first,budget,true);
carried=budget;
if(ship.FuelMain is {} capacity&&carried>capacity)return fallback;
}
catch(InvalidOperationException){return fallback;}
}
return fallback;
}
}
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Navigation;
public sealed record FlightFuelBudget(double NextJump,double UntilRefuel,bool UsesMassReference);
public static class NavigationFuelBudget
{
public static FlightFuelBudget Calculate(PlayerState player,ShipRecord ship,FsdReference drive,double fuel,double reserve)
{
var route=player.Navigation.Route;
var current=route.ToList().FindIndex(w=>w.SystemAddress==player.Navigation.CurrentSystemAddress);
var legs=new List<double>();
if(current>=0)for(var i=current+1;i<route.Count;i++)
{
legs.Add(route[i-1].Position.DistanceTo(route[i].Position));
if(route[i].Scoopable&&NavigationPreviewBuilder.HasScoop(ship))break;
}
var maximum=drive.MaxFuelPerJump;
var fallback=new FlightFuelBudget(maximum,maximum*Math.Max(1,legs.Count)+reserve+.25,false);
if(legs.Count==0||!player.CargoReliable||ship.UnladenMass is not >0||player.Status?.FuelReserve is not {} auxiliary||!double.IsFinite(fuel))return fallback;
var dry=ship.UnladenMass.Value+player.CargoCount+auxiliary;
var carried=fuel;double first=maximum,budget=fallback.UntilRefuel;
for(var iteration=0;iteration<12;iteration++)
{
try
{
// Do not subtract earlier burns from mass: later costs stay conservative.
var costs=legs.Select(distance=>Math.Min(maximum,NavigationPreviewBuilder.EstimateFuel(drive,dry+carried,distance)*1.04+.01)).ToArray();
first=costs[0];budget=costs.Sum()+reserve+.25;
if(budget<=carried+.001)return new(first,budget,true);
carried=budget;
if(ship.FuelMain is {} capacity&&carried>capacity)return fallback;
}
catch(InvalidOperationException){return fallback;}
}
return fallback;
}
}