XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json.Serialization;
using XFEEDToolBox.Core.Models;

namespace XFEEDToolBox.Core.Navigation;

public sealed record FlightStopSnapshot(string CommanderId,long? ShipId,DateTimeOffset? LoadGameAt,string GameMode,bool InGame,bool Docked,int CargoCount,
    long? CurrentSystemAddress,long? SelectedSystemAddress,int RouteCount,long[] RoutePrefix,long? RouteDestination,bool JumpInProgress,
    long? JumpTargetSystemAddress,string JumpTargetSystem,DateTimeOffset? JumpStartedAt,DateTimeOffset? LastJumpAt)
{
    public static FlightStopSnapshot Capture(PlayerState state)=>new(state.CommanderId,state.CurrentShipId,state.LoadGameAt,state.GameMode,state.InGame,state.Docked,state.CargoCount,
        state.Navigation.CurrentSystemAddress,state.Navigation.TargetSystemAddress,state.Navigation.Route.Count,state.Navigation.Route.Take(16).Select(point=>point.SystemAddress).ToArray(),
        state.Navigation.Route.LastOrDefault()?.SystemAddress,state.Navigation.JumpInProgress,state.Navigation.JumpTargetSystemAddress,state.Navigation.JumpTargetSystem,
        state.Navigation.JumpStartedAt,state.Navigation.LastJumpAt);
}

/// <summary>The first cause is immutable; explicit manual cancellation can add a veto without erasing that evidence.</summary>
public sealed record FlightAutomationStopRecord(string RunId,string Source,DateTimeOffset At,FlightAutomationEntry? Entry,FlightAutomationStage Stage,
    bool ParentTokenCancelled,string Reason,string? CauseType,FlightStopSnapshot? Snapshot)
{
    [JsonIgnore]public Exception? Cause{get;init;}
    public bool ManualVeto{get;init;}
    public DateTimeOffset? ManualVetoAt{get;init;}
}

/// <summary>Read-only proof that this runner already released inputs for this exact, bounded Journal jump.</summary>
public sealed record InputFreeJumpSnapshot(string CommanderId,long ShipId,DateTimeOffset LoadGameAt,string GameMode,long OriginSystemAddress,
    long TargetSystemAddress,string TargetSystem,DateTimeOffset JumpStartedAt,DateTimeOffset WaitingSince,bool InputsReleased)
{
    public static readonly TimeSpan MaximumWait=TimeSpan.FromSeconds(75);
    public DateTimeOffset Deadline=>WaitingSince+MaximumWait;
    public bool Matches(PlayerState current,DateTimeOffset now)=>InputsReleased&&now>=WaitingSince&&now<=Deadline
        &&current.InGame&&!current.Docked&&current.CommanderId==CommanderId&&current.CurrentShipId==ShipId&&current.LoadGameAt==LoadGameAt&&current.GameMode==GameMode
        &&!NavigationSafety.UnhealthyHull(current)&&!NavigationSafety.HasExternalThreat(current)
        &&current.Navigation.CurrentSystemAddress==OriginSystemAddress&&current.Navigation.JumpInProgress
        &&current.Navigation.JumpTargetSystemAddress==TargetSystemAddress&&string.Equals(current.Navigation.JumpTargetSystem,TargetSystem,StringComparison.OrdinalIgnoreCase)
        &&current.Navigation.JumpStartedAt==JumpStartedAt;
    public static InputFreeJumpSnapshot? AfterSuccessfulRelease(PlayerState initial,PlayerState current,NavWaypoint actualTarget,
        DateTimeOffset runJournalStartedAt,DateTimeOffset waitingSince)
    {
        if(string.IsNullOrWhiteSpace(initial.CommanderId)||initial.CurrentShipId is not >=0||initial.LoadGameAt is not {} loaded
            ||current.Navigation.CurrentSystemAddress is not >0||current.Navigation.JumpStartedAt is not {} started||started<runJournalStartedAt||started<loaded
            ||current.Navigation.JumpTargetSystemAddress!=actualTarget.SystemAddress||!string.Equals(current.Navigation.JumpTargetSystem,actualTarget.Name,StringComparison.OrdinalIgnoreCase))return null;
        var snapshot=new InputFreeJumpSnapshot(initial.CommanderId,initial.CurrentShipId.Value,loaded,initial.GameMode,current.Navigation.CurrentSystemAddress.Value,
            actualTarget.SystemAddress,actualTarget.Name,started,waitingSince,true);
        return snapshot.Matches(current,waitingSince)?snapshot:null;
    }
}