using System.Globalization; using System.Text.Json; using XFEEDToolBox.Core.Localization; using XFEEDToolBox.Core.Models; namespace XFEEDToolBox.Core.Galaxy; public enum GatheringCategory { Manufactured, Raw, Encoded } public sealed record GatheringMaterial(string Name) { public string DisplayName=>ChineseNames.Translate(Name) is var n&&n!="未收录名称"?n:Name; public string OriginalName=>DisplayName==Name?"":Name; } public sealed record ManufacturedGatheringRule(string Material,string[] Allegiances,string[] States) { public string Description=>string.Join(" / ",Allegiances)+" · "+string.Join(" / ",States); } public sealed record RawGatheringSite(string Name,string SystemName,Coordinates Position,long SystemAddress,string Body, double Latitude,double Longitude,double FinishLatitude,double FinishLongitude,string[] Materials) { public GalacticPlace Place(Coordinates origin)=>new(Name,SystemName,Position,SystemAddress,origin.DistanceTo(Position), FormattableString.Invariant($"{Body} · 起点 {Latitude:F2}°, {Longitude:F2}° · 终点 {FinishLatitude:F2}°, {FinishLongitude:F2}°"),"用户提供的脑树采集点 · 地表坐标见原图",Source:"脑树采集点目录"); } /// Collection locations and HGE conditions supplied by the user. Does not create or execute a flight route. public sealed class MaterialGathering(HttpClient http) { public static IReadOnlyList Manufactured {get;}=[ new("Core Dynamics Composites",["Federation"],["Election","None"]), new("Military Grade Alloys",["Independent","Alliance"],["War","Civil War"]), new("Military Supercapacitors",["Independent","Alliance"],["War","Civil War"]), new("Improvised Components",["Independent","Alliance"],["Civil Unrest"]), new("Imperial Shielding",["Empire"],["Election","None"]), new("Pharmaceutical Isolators",["Independent","Alliance"],["Outbreak"]), new("Proto Heat Radiators",["Independent","Alliance"],["Boom","None"]), new("Proto Radiolic Alloys",["Independent","Alliance"],["Boom","None"])]; public static IReadOnlyList RawSites {get;}=[ new("Selenium Smudge","HR 3230",new(394.15625,19.25,-152.375),182359951707,"3 a a",52.64,-165.74,52.71,-165.93,["Selenium","Manganese","Zirconium"]), new("Triple T Ridge","Wregoe SB-Z b41-2",new(415.375,19.40625,-145.1875),5074638218601,"A 1 a",-50.94,165.18,-51.02,164.65,["Technetium","Tin","Tungsten","Chromium","Manganese","Zinc"]), new("Antimony Snow Man","HIP 34958",new(369.1875,-109.28125,-68.625),491597564259,"AB 1 a",-15.73,35.43,-15.61,35.49,["Antimony","Niobium","Tungsten","Chromium","Germanium","Vanadium"]), new("Ruthenium Double Dot","HIP 34958",new(369.1875,-109.28125,-68.625),491597564259,"AB 1 d a",6.70,-47.07,6.62,-47.42,["Ruthenium","Tin","Mercury","Chromium","Germanium","Zinc"]), new("Tellurium Valley","Synuefe FS-X b48-1",new(364.09375,-89.84375,-13.71875),2874809263521,"A 1 a",39.99,72.39,40.16,72.27,["Tellurium","Cadmium","Tungsten","Chromium","Germanium","Zirconium"]), new("Polonium Tire Tread","Synuefe BY-F d12-39",new(381.3125,-71.4375,-7.25),1350591039851,"A 2",35.60,45.99,35.76,46.79,["Polonium","Molybdenum","Mercury","Chromium","Zinc","Vanadium"]), new("Yttrium Pepsi Crater","Synuefe AN-H d11-101",new(432.625,-129.28125,-90.15625),3480911579491,"AB 1 c",65.57,-46.12,65.60,-46.27,["Yttrium","Cadmium","Niobium","Chromium","Manganese"])]; public static IReadOnlyList Choices(GatheringCategory category)=>(category switch { GatheringCategory.Manufactured=>Manufactured.Select(r=>r.Material), GatheringCategory.Raw=>RawSites.SelectMany(r=>r.Materials), _=>Enumerable.Empty() }).Distinct(StringComparer.OrdinalIgnoreCase).Order(StringComparer.OrdinalIgnoreCase).Select(n=>new GatheringMaterial(n)).ToArray(); public static IReadOnlyList FixedPlaces(GatheringCategory category,string material,Coordinates origin) { if(!origin.IsValid)throw new ArgumentException("尚未读取当前位置。"); if(category==GatheringCategory.Encoded) return [new("Jameson 坠毁点","HIP 12099",new(-101.90625,-95.46875,-165.59375),560216394075, origin.DistanceTo(new(-101.90625,-95.46875,-165.59375)),"1 B · 纬度 -54.3803° · 经度 -50.3575° · 扫描坠毁点数据信标", "Jameson Crash Site · 地表数据采集点",Source:"Jameson 坠毁点目录")]; if(category!=GatheringCategory.Raw)throw new ArgumentException("该类别需要实时搜索。"); return RawSites.Where(s=>s.Materials.Contains(material,StringComparer.OrdinalIgnoreCase)).Select(s=>s.Place(origin)).OrderBy(p=>p.Distance).ThenBy(p=>p.Name).ToArray(); } public static ManufacturedGatheringRule Rule(string material)=>Manufactured.FirstOrDefault(r=>r.Material.Equals(material,StringComparison.OrdinalIgnoreCase))??throw new ArgumentException("请选择表内的 G5 人造材料。"); public static object Request(string material,Coordinates origin) { if(!origin.IsValid)throw new ArgumentException("尚未读取当前位置。"); var rule=Rule(material); return new{filters=new{allegiance=new{value=rule.Allegiances},minor_faction_presences=new[]{new{state=new{value=rule.States}}}}, sort=new[]{new{distance=new{direction="asc"}}},size=20,page=0,reference_coords=new{x=origin.X,y=origin.Y,z=origin.Z}}; } public async Task> SearchManufacturedAsync(string material,Coordinates origin,CancellationToken token=default) { using var json=await PlaceSearchHttp.ReadAsync(http,"https://spansh.co.uk/api/systems/search",Request(material,origin),token).ConfigureAwait(false); token.ThrowIfCancellationRequested();return ParseManufactured(json.RootElement,material,origin); } public static IReadOnlyList ParseManufactured(JsonElement root,string material,Coordinates origin) { var rule=Rule(material); if(!root.TryGetProperty("results",out var rows)||rows.ValueKind!=JsonValueKind.Array)throw new InvalidDataException("材料搜索未返回有效结果。"); var found=new List(); foreach(var row in rows.EnumerateArray().Take(100)) { string S(string key)=>row.TryGetProperty(key,out var v)&&v.ValueKind==JsonValueKind.String?v.GetString()??"":""; var allegiance=S("allegiance"); if(!rule.Allegiances.Contains(allegiance,StringComparer.OrdinalIgnoreCase)||S("name").Length==0)continue; string? state=null;string faction=""; if(row.TryGetProperty("minor_faction_presences",out var factions)&&factions.ValueKind==JsonValueKind.Array) foreach(var presence in factions.EnumerateArray()) { // Only the current faction state qualifies; pending/recovering states do not. if(!presence.TryGetProperty("state",out var current)||current.ValueKind!=JsonValueKind.String)continue; var value=current.GetString()!;if(!rule.States.Contains(value,StringComparer.OrdinalIgnoreCase))continue; state=value;faction=presence.TryGetProperty("name",out var n)&&n.ValueKind==JsonValueKind.String?n.GetString()??"":"";break; } if(state is null)continue; if(!row.TryGetProperty("x",out var x)||!x.TryGetDouble(out var xx)||!row.TryGetProperty("y",out var y)||!y.TryGetDouble(out var yy)||!row.TryGetProperty("z",out var z)||!z.TryGetDouble(out var zz))continue; var position=new Coordinates(xx,yy,zz);if(!position.IsValid)continue; long? id=row.TryGetProperty("id64",out var a)&&a.TryGetInt64(out var address)&&address>0?address:null; var updated=S("updated_at");var stamp=DateTimeOffset.TryParse(updated,CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal,out var at)?$"势力记录于 {at.ToLocalTime():MM-dd HH:mm}":"势力更新时间未提供"; found.Add(new(material+" · 高等级信号候选",S("name"),position,id,origin.DistanceTo(position),allegiance+" · "+faction+" · "+state,stamp+" · 到达后扫描 HGE,掉落以游戏为准",Source:"Spansh 材料候选星系")); } return found.DistinctBy(p=>p.SystemName,StringComparer.OrdinalIgnoreCase).OrderBy(p=>p.Distance).Take(1).ToArray(); } }