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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

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

namespace XFEEDToolBox.Core.Presentation;

public sealed record ShipStatusValue(string Key,string Name,string Original,string Value,string Source);
public sealed class ShipStatusLiveRow(ShipStatusValue value):LiveRow<ShipStatusValue>(value)
{
    public string Name=>Record.Name;
    public string Original=>Record.Original;
    public string Value=>Record.Value;
    public string Source=>Record.Source;
}
public sealed record ShipAmmunition(string Slot,string Name,string Original,string Clip,string Reserve,string State,string Source);
public sealed class ShipAmmunitionLiveRow(ShipAmmunition value):LiveRow<ShipAmmunition>(value)
{
    public string Slot=>Record.Slot;
    public string Name=>Record.Name;
    public string Original=>Record.Original;
    public string Clip=>Record.Clip;
    public string Reserve=>Record.Reserve;
    public string State=>Record.State;
    public string Source=>Record.Source;
}

/// <summary>Formats observed values and their actual sources; an absent measurement stays unknown.</summary>
public static class ShipStatusRows
{
    public static string At(DateTimeOffset? at)=>at?.ToLocalTime().ToString("HH:mm:ss",CultureInfo.InvariantCulture)??"无记录";
    private static string F(double? value,string format="F1",string suffix="")=>value is {} n&&double.IsFinite(n)?n.ToString(format,CultureInfo.InvariantCulture)+suffix:"—";
    private static double? N(JsonElement value,string key)=>value.ValueKind==JsonValueKind.Object&&value.TryGetProperty(key,out var property)&&property.ValueKind==JsonValueKind.Number&&property.TryGetDouble(out var n)&&double.IsFinite(n)?n:null;
    private static JsonElement Object(JsonElement value,string key)=>value.ValueKind==JsonValueKind.Object&&value.TryGetProperty(key,out var property)&&property.ValueKind==JsonValueKind.Object?property:default;
    private static double? Radius(JsonElement geometry)
    {
        if(geometry.ValueKind!=JsonValueKind.Object||!geometry.TryGetProperty("position",out var p)||p.ValueKind!=JsonValueKind.Array||p.GetArrayLength()!=3)return null;
        var a=p.EnumerateArray().Select(v=>v.GetDouble()).ToArray();return Math.Sqrt(a.Sum(v=>v*v));
    }
    public static IReadOnlyList<ShipStatusValue> Flight(JsonElement? native)
    {
        var raw=native??default;var fsd=Object(raw,"fsd");var thermal=Object(raw,"thermal");var geometry=Object(raw,"geometry");var scoop=Object(geometry,"scoop");
        var time=N(raw,"observedAtUnixMilliseconds");var source="原生 · "+At(time is {} t?DateTimeOffset.FromUnixTimeMilliseconds((long)t):null);
        var radius=Radius(geometry);var exclusion=N(geometry,"exclusionRadius");var max=N(scoop,"maximumRate");var efficiency=N(scoop,"efficiency");
        var active=scoop.ValueKind==JsonValueKind.Object&&scoop.TryGetProperty("active",out var a)&&a.ValueKind==JsonValueKind.True;
        var scoopKnown=scoop.ValueKind==JsonValueKind.Object&&scoop.TryGetProperty("verified",out var sk)&&sk.ValueKind==JsonValueKind.True;
        var hud=N(raw,"heatHudPercent");var verified=raw.ValueKind==JsonValueKind.Object&&raw.TryGetProperty("heatNormalizationVerified",out var v)&&v.ValueKind==JsonValueKind.True;
        var state=N(fsd,"state");var clock=N(fsd,"clock");var duration=N(fsd,"duration");
        var rows=new List<ShipStatusValue>();
        void Add(string key,string name,string original,string value)=>rows.Add(new(key,name,original,value,source));
        Add("heat","舰体热量","Heat",verified?F(hud,"F1","%" ):"待核对");
        Add("heatRaw","内部热比例","Internal heat ratio",F(N(raw,"internalHeatPercent")??N(raw,"heatPercent"),"F2","%"));
        Add("forecast","充能预测上界","Charge forecast",F(N(thermal,"fullChargeAndHoldPeak"),"F1",verified?"%":"% · 内部比例"));
        var speed=N(raw,"speedMetresPerSecond");
        Add("speed","实际速度","Speed",speed>=1e6?F(speed/1e6,"F2"," Mm/s"):F(speed,"F0"," m/s"));
        Add("throttle","节流阀","Throttle",F(N(fsd,"throttle")*100,"F0","%"));
        Add("angular","转向角速度","Angular speed",F(N(raw,"angularSpeedDegreesPerSecond"),"F2"," °/s"));
        Add("fsd","跃迁引擎","Frame shift drive",state switch{0=>"未就绪",1=>"冷却中",2=>"就绪",3=>clock>=duration?"充能完成":"充能中",4=>"跃迁中",_=>"—"});
        Add("charge","剩余充能","Charge remaining",state==3?F(Math.Max(0,(duration??0)-(clock??0)),"F1"," s"):"—");
        Add("scoop","当前吸油速率","Fuel scoop rate",F(!scoopKnown?null:active?max*efficiency*1000:0,"F1"," kg/s"));
        Add("scoopMax","额定吸油速率","Maximum scoop rate",F(max*1000,"F0"," kg/s"));
        Add("efficiency","吸油效率","Scoop efficiency",F(!scoopKnown?null:active?efficiency*100:0,"F1","%"));
        Add("radius","距恒星中心","Distance to star centre",F(radius/1e6,"F2"," Mm"));
        Add("exclusion","超巡碰撞半径","Cruise exclusion radius",F(exclusion/1e6,"F2"," Mm"));
        Add("ratio","当前 / 碰撞半径","Radius ratio",exclusion>0?F(radius/exclusion,"F4"," ×"):"—");
        Add("fullRadius","满功率外沿半径","Full-rate outer radius",F(N(scoop,"fullRateRadius")/1e6,"F2"," Mm"));
        return rows;
    }
    public static IReadOnlyList<ShipStatusValue> Cockpit(PlayerState p)
    {
        var ship=p.Ships.FirstOrDefault(s=>s.ShipId==p.CurrentShipId);var status=p.Status;var flags=status?.Flags;var source="Status · "+At(p.StatusAt);
        string Flag(int bit,string yes,string no)=>flags is {} f?(f&(1L<<bit))!=0?yes:no:"—";
        var rows=new List<ShipStatusValue>();
        void Add(string key,string name,string original,string value,string? from=null)=>rows.Add(new(key,name,original,value,from??source));
        var fuel=p.Navigation.FuelAt>p.StatusAt?p.Navigation.MainFuel:status?.FuelMain??p.Navigation.MainFuel;
        Add("fuel","主油箱","Main fuel",F(fuel,"F2"," t")+" / "+F(ship?.FuelMain,"F2"," t"));
        Add("reserve","备用油箱","Fuel reservoir",F(status?.FuelReserve,"F3"," t"));
        Add("shield","护盾","Shields",Flag(3,"已升起","已失效"));
        Add("hull","船体完整度","Hull integrity",F(ship?.HullHealth*100,"F1","%"),"Loadout 快照 · "+At(ship?.ConfigurationAt));
        Add("pips","系统 / 引擎 / 武器配电","SYS / ENG / WEP",status?.Pips.Count==3?string.Join(" / ",status.Pips.Select(n=>(n/2d).ToString("g",CultureInfo.InvariantCulture))):"—");
        Add("cargo","货舱","Cargo",$"{p.CargoCount} / {p.CargoCapacity} t");
        Add("flight","飞行模式","Flight mode",Flag(4,"超巡","普通空间"));
        Add("assist","飞行辅助","Flight assist",Flag(5,"关闭","开启"));
        Add("gear","起落架","Landing gear",Flag(2,"展开","收起"));
        Add("hardpoints","武器挂点","Hardpoints",Flag(6,"部署","收起"));
        Add("scoopDoor","货舱舱门","Cargo scoop",Flag(9,"展开","收起"));
        Add("silent","静默航行","Silent running",Flag(10,"开启","关闭"));
        Add("masslock","质量锁定","Mass locked",Flag(16,"是","否"));
        Add("danger","危险状态","In danger",Flag(22,"警报","正常"));
        Add("heatWarning","过热警报","Overheating",Flag(20,"警报","正常"));
        Add("lowFuel","低燃油警报","Low fuel",Flag(19,"警报","正常"));
        Add("latitude","纬度","Latitude",F(status?.Latitude,"F5","°"));
        Add("longitude","经度","Longitude",F(status?.Longitude,"F5","°"));
        Add("altitude","海拔","Altitude",F(status?.Altitude,"F1"," m"));
        Add("heading","航向","Heading",F(status?.Heading,"F1","°"));
        Add("range","最大跳距","Maximum jump range",F(ship?.MaxJumpRange,"F2"," ly"),"Loadout 快照 · "+At(ship?.ConfigurationAt));
        Add("rebuy","保险费用","Rebuy",ship?.Rebuy is {} rebuy?rebuy.ToString("N0",CultureInfo.InvariantCulture)+" Cr":"—","Loadout 快照 · "+At(ship?.ConfigurationAt));
        return rows;
    }
    public static IReadOnlyList<ShipAmmunition> Ammunition(PlayerState p)
    {
        var ship=p.Ships.FirstOrDefault(s=>s.ShipId==p.CurrentShipId);
        return ship?.Modules.Where(m=>m.AmmoInClip is not null||m.AmmoInHopper is not null).Select(m=>new ShipAmmunition(m.Slot,m.DisplayName,m.OriginalName,m.AmmoInClip?.ToString()??"—",m.AmmoInHopper?.ToString()??"—",$"{m.State} · 完整度 {m.HealthText}","Loadout 快照 · "+At(ship.ConfigurationAt))).ToArray()??[];
    }
}