using XFEEDToolBox.Core.Engineering;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Localization;
namespace XFEEDToolBox.Core.Planning;
public sealed record UpgradeListEntry(Guid Id,FittedModule Module,RefitTarget Target,int Quantity,string SourceKey="",bool Purchase=false,decimal? UnitPrice=null,string PriceSource="手动报价")
{
public string Name=>Module.DisplayName;
public string OriginalName=>Module.OriginalName;
public string TargetName=>$"{ChineseNames.Translate(Target.Blueprint)} · G{Target.Grade} · {Target.Quality:P0} · "+(Target.Experimental.Length>0?ChineseNames.Translate(Target.Experimental):"无实验效果");
public string TargetOriginal=>ChineseNames.English(Target.Blueprint)+" · "+(Target.Experimental.Length>0?ChineseNames.English(Target.Experimental):"None");
public string Detail=>$"{Quantity} 件 · {(SourceKey.Length>0?"使用现有模块进度":"从未改装开始")}"+(Purchase?(UnitPrice is {} price?$" · {price:N0} Cr/件 · {PriceSource}":" · 购买价待报价"):" · 仅工程材料");
}
public sealed record UpgradeListBudget(ShipRefitResult Engineering,decimal KnownCredits,int UnquotedItems,decimal? AvailableCredits)
{
public decimal? MissingCredits=>AvailableCredits is {} cash?Math.Max(0,KnownCredits-cash):null;
}
public static class UpgradeListPlanner
{
public static IReadOnlyList<UpgradeListEntry> AddShip(IReadOnlyList<UpgradeListEntry> entries,ShipRefitDocument document)
{
var requested=document.Slots.Where(s=>s.Module is not null&&s.Target is not null).ToArray();
if(requested.Length==0)throw new InvalidOperationException("请先为模块设定工程改装目标,再添加至清单。");
var result=entries.ToList();
foreach(var slot in requested)
{
var source=slot.InstanceKey;
var added=new UpgradeListEntry(Guid.NewGuid(),slot.Module!,slot.Target!,1,source,source.Length==0,source.Length==0?ModulePrices.Find(slot.Module!.Item):null,"模块参考基础价");
var existing=source.Length>0?result.FindIndex(e=>e.SourceKey==source):-1;
if(existing>=0)result[existing]=added with{Id=result[existing].Id};else result.Add(added);
}
if(result.Sum(e=>e.Quantity)>100)throw new InvalidOperationException("清单最多支持 100 件模块。");
return result;
}
public static UpgradeListBudget Calculate(IReadOnlyList<UpgradeListEntry> entries,PlayerState p,EngineeringCatalog catalog,CancellationToken token=default)
{
if(entries.Sum(e=>e.Quantity)>100||entries.Any(e=>e.Quantity is <1 or >100||e.UnitPrice<0))throw new InvalidOperationException("清单最多支持 100 件模块,数量与价格必须有效。");
var slots=new List<RefitSlot>();var owned=new HashSet<string>();decimal credits=0;var unknown=0;
foreach(var entry in entries)
{
var module=entry.Module;
if(entry.SourceKey.Length>0)
{
if(entry.Quantity!=1||!owned.Add(entry.SourceKey))throw new InvalidOperationException("同一件已有模块只能在清单中使用一次。");
var parts=entry.SourceKey.Split(':',3);
if(parts.Length==3&&parts[0]=="ship"&&long.TryParse(parts[1],out var id))
{
var ship=p.Ships.FirstOrDefault(s=>s.ShipId==id);
if(ship is null||ship.ConfigurationChanged||ship.ConfigurationAt is null)throw new InvalidOperationException("清单中已有模块的完整装配尚未读取。");
module=ship.Modules.FirstOrDefault(m=>m.Slot==parts[2]&&m.Item.Equals(entry.Module.Item,StringComparison.OrdinalIgnoreCase))??throw new InvalidOperationException("清单中的已有模块已被卸下或更换,请重新选择。");
}
else if(parts.Length==2&&parts[0]=="store"&&long.TryParse(parts[1],out var storage))
{
var stored=p.StoredModules.FirstOrDefault(s=>s.StorageSlot==storage&&s.Item.Equals(module.Item,StringComparison.OrdinalIgnoreCase)&&!s.InTransit)
??throw new InvalidOperationException("清单中的寄存模块已移动或更换,请重新选择。");
module=module with{Engineering=stored.Engineering.Length==0?null:new(){Blueprint=stored.Engineering,Level=stored.EngineeringLevel??0,Quality=stored.EngineeringQuality}};
}
else throw new InvalidOperationException("清单中的模块来源无效。");
}
for(var n=0;n<entry.Quantity;n++)slots.Add(new(){Slot=$"list:{entry.Id}:{n}",Module=module,Target=entry.Target,Origin="全局改装清单"});
if(entry.Purchase){if(entry.UnitPrice is {} unit)credits=checked(credits+unit*entry.Quantity);else unknown+=entry.Quantity;}
}
return new(ShipRefitPlanner.Calculate(new(0,"","全局改装清单",null,slots),catalog,p.Engineers,p.Materials,token),credits,unknown,p.CreditsReliable?p.Credits:null);
}
}
using XFEEDToolBox.Core.Engineering;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Localization;
namespace XFEEDToolBox.Core.Planning;
public sealed record UpgradeListEntry(Guid Id,FittedModule Module,RefitTarget Target,int Quantity,string SourceKey="",bool Purchase=false,decimal? UnitPrice=null,string PriceSource="手动报价")
{
public string Name=>Module.DisplayName;
public string OriginalName=>Module.OriginalName;
public string TargetName=>$"{ChineseNames.Translate(Target.Blueprint)} · G{Target.Grade} · {Target.Quality:P0} · "+(Target.Experimental.Length>0?ChineseNames.Translate(Target.Experimental):"无实验效果");
public string TargetOriginal=>ChineseNames.English(Target.Blueprint)+" · "+(Target.Experimental.Length>0?ChineseNames.English(Target.Experimental):"None");
public string Detail=>$"{Quantity} 件 · {(SourceKey.Length>0?"使用现有模块进度":"从未改装开始")}"+(Purchase?(UnitPrice is {} price?$" · {price:N0} Cr/件 · {PriceSource}":" · 购买价待报价"):" · 仅工程材料");
}
public sealed record UpgradeListBudget(ShipRefitResult Engineering,decimal KnownCredits,int UnquotedItems,decimal? AvailableCredits)
{
public decimal? MissingCredits=>AvailableCredits is {} cash?Math.Max(0,KnownCredits-cash):null;
}
public static class UpgradeListPlanner
{
public static IReadOnlyList<UpgradeListEntry> AddShip(IReadOnlyList<UpgradeListEntry> entries,ShipRefitDocument document)
{
var requested=document.Slots.Where(s=>s.Module is not null&&s.Target is not null).ToArray();
if(requested.Length==0)throw new InvalidOperationException("请先为模块设定工程改装目标,再添加至清单。");
var result=entries.ToList();
foreach(var slot in requested)
{
var source=slot.InstanceKey;
var added=new UpgradeListEntry(Guid.NewGuid(),slot.Module!,slot.Target!,1,source,source.Length==0,source.Length==0?ModulePrices.Find(slot.Module!.Item):null,"模块参考基础价");
var existing=source.Length>0?result.FindIndex(e=>e.SourceKey==source):-1;
if(existing>=0)result[existing]=added with{Id=result[existing].Id};else result.Add(added);
}
if(result.Sum(e=>e.Quantity)>100)throw new InvalidOperationException("清单最多支持 100 件模块。");
return result;
}
public static UpgradeListBudget Calculate(IReadOnlyList<UpgradeListEntry> entries,PlayerState p,EngineeringCatalog catalog,CancellationToken token=default)
{
if(entries.Sum(e=>e.Quantity)>100||entries.Any(e=>e.Quantity is <1 or >100||e.UnitPrice<0))throw new InvalidOperationException("清单最多支持 100 件模块,数量与价格必须有效。");
var slots=new List<RefitSlot>();var owned=new HashSet<string>();decimal credits=0;var unknown=0;
foreach(var entry in entries)
{
var module=entry.Module;
if(entry.SourceKey.Length>0)
{
if(entry.Quantity!=1||!owned.Add(entry.SourceKey))throw new InvalidOperationException("同一件已有模块只能在清单中使用一次。");
var parts=entry.SourceKey.Split(':',3);
if(parts.Length==3&&parts[0]=="ship"&&long.TryParse(parts[1],out var id))
{
var ship=p.Ships.FirstOrDefault(s=>s.ShipId==id);
if(ship is null||ship.ConfigurationChanged||ship.ConfigurationAt is null)throw new InvalidOperationException("清单中已有模块的完整装配尚未读取。");
module=ship.Modules.FirstOrDefault(m=>m.Slot==parts[2]&&m.Item.Equals(entry.Module.Item,StringComparison.OrdinalIgnoreCase))??throw new InvalidOperationException("清单中的已有模块已被卸下或更换,请重新选择。");
}
else if(parts.Length==2&&parts[0]=="store"&&long.TryParse(parts[1],out var storage))
{
var stored=p.StoredModules.FirstOrDefault(s=>s.StorageSlot==storage&&s.Item.Equals(module.Item,StringComparison.OrdinalIgnoreCase)&&!s.InTransit)
??throw new InvalidOperationException("清单中的寄存模块已移动或更换,请重新选择。");
module=module with{Engineering=stored.Engineering.Length==0?null:new(){Blueprint=stored.Engineering,Level=stored.EngineeringLevel??0,Quality=stored.EngineeringQuality}};
}
else throw new InvalidOperationException("清单中的模块来源无效。");
}
for(var n=0;n<entry.Quantity;n++)slots.Add(new(){Slot=$"list:{entry.Id}:{n}",Module=module,Target=entry.Target,Origin="全局改装清单"});
if(entry.Purchase){if(entry.UnitPrice is {} unit)credits=checked(credits+unit*entry.Quantity);else unknown+=entry.Quantity;}
}
return new(ShipRefitPlanner.Calculate(new(0,"","全局改装清单",null,slots),catalog,p.Engineers,p.Materials,token),credits,unknown,p.CreditsReliable?p.Credits:null);
}
}