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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Core.Navigation;

public enum SystemOrbitQuality { Unavailable, ElementsOnly, NativeVerified }
public sealed record SystemOrbitParent(string Kind,int BodyId);

/// <summary>
/// An orbit's provenance is independent of its instantaneous body position.
/// Directions, when verified, are system-space unit vectors pointing towards
/// periapsis and positive eccentric anomaly respectively. Distances are metres.
/// </summary>
public sealed record SystemOrbit
{
    public long SystemAddress{get;init;}
    public ulong ChildWorldAddress{get;init;}
    public string ChildIdentity{get;init;}="";
    public ulong? ParentWorldAddress{get;init;}
    public string? ParentIdentity{get;init;}
    public Vector3d? ParentPosition{get;init;}
    public string ParentName{get;init;}="";
    public bool ParentIsBarycentre{get;init;}
    public int? JournalBodyId{get;init;}
    public IReadOnlyList<SystemOrbitParent> JournalParents{get;init;}=[];
    public double? SemiMajorAxisMetres{get;init;}
    public double? Eccentricity{get;init;}
    public double? OrbitalInclinationDegrees{get;init;}
    public double? ArgumentOfPeriapsisDegrees{get;init;}
    public double? AscendingNodeDegrees{get;init;}
    public double? PeriodSeconds{get;init;}
    public double? MeanAnomalyDegrees{get;init;}
    public DateTimeOffset? Epoch{get;init;}
    public Vector3d? PeriapsisDirection{get;init;}
    public Vector3d? TransverseDirection{get;init;}
    public string Source{get;init;}="";
    public SystemOrbitQuality Quality{get;init;}
    public string UnavailableReason{get;init;}="轨道参数尚未读取。";

    public bool HasEllipticElements=>SemiMajorAxisMetres is {} a&&a>0&&double.IsFinite(a)
        &&Eccentricity is {} e&&e>=0&&e<1&&double.IsFinite(e);
    public bool Renderable=>Quality==SystemOrbitQuality.NativeVerified&&HasEllipticElements&&SystemAddress>0
        &&ChildWorldAddress!=0&&ParentWorldAddress is >0&&ParentWorldAddress!=ChildWorldAddress
        &&ChildIdentity.Length>0&&!string.IsNullOrEmpty(ParentIdentity)&&ParentIdentity!=ChildIdentity
        &&PeriapsisDirection is {Finite:true} x&&TransverseDirection is {Finite:true} y
        &&Math.Abs(x.Length-1)<1e-5&&Math.Abs(y.Length-1)<1e-5&&Math.Abs(Vector3d.Dot(x,y))<1e-5;
}

/// <summary>
/// Retains real Scan fields. Journal angles do not establish a transform to the
/// game's current system XYZ frame, so they can never authorize a drawn orbit.
/// </summary>
public sealed class JournalSystemOrbitCatalogue
{
    private sealed record Entry(long System,int BodyId,string Name,bool Star,DateTimeOffset At,SystemOrbit Orbit);
    private readonly object gate=new();
    private readonly Dictionary<(long System,int BodyId),Entry> entries=[];

    public void Apply(JsonElement value)
    {
        var eventName=value.Text("event");
        if(eventName is not ("Scan" or "ScanBaryCentre")||value.Long("SystemAddress") is not >0||value.Time() is not {} at)return;
        var body=value.Get("BodyID");
        if(body.ValueKind!=JsonValueKind.Number||!body.TryGetInt32(out var id)||id<0)return;
        var system=value.Long("SystemAddress");
        var parents=new List<SystemOrbitParent>();var rawParents=value.Get("Parents");
        if(rawParents.ValueKind==JsonValueKind.Array)
        {
            if(rawParents.GetArrayLength()>32)return;
            foreach(var parent in rawParents.EnumerateArray())
            {
                if(parent.ValueKind!=JsonValueKind.Object)return;
                var props=parent.EnumerateObject().ToArray();
                if(props.Length!=1||props[0].Name is not ("Star" or "Planet" or "Null")
                    ||props[0].Value.ValueKind!=JsonValueKind.Number||!props[0].Value.TryGetInt32(out var parentId)||parentId<0)return;
                parents.Add(new(props[0].Name,parentId));
            }
        }
        var orbit=new SystemOrbit
        {
            SystemAddress=system,JournalBodyId=id,JournalParents=parents.ToArray(),Epoch=at,
            SemiMajorAxisMetres=value.Number("SemiMajorAxis"),Eccentricity=value.Number("Eccentricity"),
            OrbitalInclinationDegrees=value.Number("OrbitalInclination"),ArgumentOfPeriapsisDegrees=value.Number("Periapsis"),
            AscendingNodeDegrees=value.Number("AscendingNode"),PeriodSeconds=value.Number("OrbitalPeriod"),MeanAnomalyDegrees=value.Number("MeanAnomaly"),
            Source="Journal."+eventName,Quality=SystemOrbitQuality.ElementsOnly,
            UnavailableReason="已读取 Journal 轨道参数;与当前星系坐标的轨道平面尚未核实。"
        };
        lock(gate)
        {
            var key=(system,id);
            if(entries.TryGetValue(key,out var previous)&&previous.At>at)return;
            entries[key]=new(system,id,value.Text("BodyName"),value.Text("StarType").Length>0,at,orbit);
        }
    }

    public IReadOnlyList<SystemOrbit> Bind(CurrentSystemSnapshot snapshot)
    {
        Entry[] current;lock(gate)current=entries.Values.Where(entry=>entry.System==snapshot.SystemAddress).ToArray();
        var bodies=snapshot.Locations.Where(location=>location.Category is SystemLocationCategory.Star or SystemLocationCategory.Planet or SystemLocationCategory.LandablePlanet).ToArray();
        SystemLocation? Match(Entry entry)
        {
            if(entry.Name.Length==0)return null;
            var matches=bodies.Where(location=>location.Name.Equals(entry.Name,StringComparison.Ordinal)
                &&(location.Category==SystemLocationCategory.Star)==entry.Star).ToArray();
            return matches.Length==1?matches[0]:null;
        }
        var result=new List<SystemOrbit>();
        foreach(var child in bodies)
        {
            var matches=current.Where(entry=>ReferenceEquals(Match(entry),child)).ToArray();
            if(matches.Length!=1)
            {
                result.Add(new(){SystemAddress=snapshot.SystemAddress,ChildWorldAddress=child.WorldAddress,ChildIdentity=child.Identity,
                    UnavailableReason=matches.Length==0?"该天体没有可唯一关联的本机轨道扫描。":"本机扫描的天体身份不唯一,未关联轨道。"});
                continue;
            }
            var entry=matches[0];var orbit=entry.Orbit with{ChildWorldAddress=child.WorldAddress,ChildIdentity=child.Identity};
            if(orbit.JournalParents.FirstOrDefault() is {} parent)
            {
                var parentEntry=current.SingleOrDefault(candidate=>candidate.BodyId==parent.BodyId);
                var parentLocation=parentEntry is null?null:Match(parentEntry);
                if(parentLocation is not null&&parentLocation.Identity!=child.Identity
                    &&(parent.Kind=="Star"&&parentLocation.Category==SystemLocationCategory.Star
                        ||parent.Kind=="Planet"&&parentLocation.Category is SystemLocationCategory.Planet or SystemLocationCategory.LandablePlanet))
                    orbit=orbit with{ParentWorldAddress=parentLocation.WorldAddress,ParentIdentity=parentLocation.Identity};
            }
            if(!orbit.HasEllipticElements)orbit=orbit with{Quality=SystemOrbitQuality.Unavailable,UnavailableReason="本机扫描没有有效的椭圆半长轴与偏心率。"};
            else if(orbit.ParentWorldAddress is null)orbit=orbit with{UnavailableReason="已读取 Journal 轨道参数;父天体或质心尚未在当前目录中唯一定位。"};
            result.Add(orbit);
        }
        return result;
    }
}