using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Planning;
public static class ShipPadPolicy
{
public static void RequireFits(Station station,string actualShipType,LandingPad manual=LandingPad.Unknown)
{
var actual=RequiredPad(actualShipType);
if(actual==LandingPad.Unknown)throw new InvalidOperationException("实际船型尺寸尚未确认,已停止执行。");
var required=(LandingPad)Math.Max((int)manual,(int)actual);var available=StationSourcePolicy.EffectiveMaxLandingPad(station);
if(available==LandingPad.Unknown||available<required)throw new InvalidOperationException($"{station.DisplayName} 的停机坪未确认能容纳当前飞船,已停止执行。");
}
// Derived from the 49 fdname/name/class entries in the bundled Outfitting/Edsy/eddb.js.
// Pinned EDSY commit: 1fafc5d9f2d07e2e92443657c83d0c65b897bb00 (2026-10-20).
// Source SHA-256: e1eb8d9f28745a8a745fa473bd1ccbb4b636aaedcd74cc23cfad9484086873d5.
// edsy.js renders ship.class with '?SML'; this is the hull size, not a module class.
private static readonly IReadOnlyDictionary<string, LandingPad> Pads = CreatePads();
public static LandingPad RequiredPad(string? actualShipType) =>
Pads.TryGetValue(Normalize(actualShipType), out var pad) ? pad : LandingPad.Unknown;
// A manual preference or advisory value can increase the requirement, never reduce
// the verified hull requirement. Callers must reject an unresolved actual hull when
// verified ship compatibility is mandatory; Unknown is not evidence of a small hull.
public static LandingPad EffectiveRequiredPad(LandingPad manual, LandingPad advisory, string? actualShipType) =>
(LandingPad)Math.Max(Math.Max((int)manual, (int)advisory), (int)RequiredPad(actualShipType));
private static IReadOnlyDictionary<string, LandingPad> CreatePads()
{
(string Type, string Name, LandingPad Pad)[] ships =
[
("SideWinder", "Sidewinder", LandingPad.Small),
("Eagle", "Eagle", LandingPad.Small),
("Hauler", "Hauler", LandingPad.Small),
("Adder", "Adder", LandingPad.Small),
("Empire_Eagle", "Imperial Eagle", LandingPad.Small),
("Viper", "Viper Mk III", LandingPad.Small),
("CobraMkIII", "Cobra Mk III", LandingPad.Small),
("Viper_MkIV", "Viper Mk IV", LandingPad.Small),
("DiamondBack", "Diamondback Scout", LandingPad.Small),
("CobraMkIV", "Cobra Mk IV", LandingPad.Small),
("Type6", "Type-6 Transporter", LandingPad.Medium),
("Dolphin", "Dolphin", LandingPad.Small),
("DiamondBackXL", "Diamondback Explorer", LandingPad.Small),
("Empire_Courier", "Imperial Courier", LandingPad.Small),
("Independant_Trader", "Keelback", LandingPad.Medium),
("Asp_Scout", "Asp Scout", LandingPad.Medium),
("Vulture", "Vulture", LandingPad.Small),
("Asp", "Asp Explorer", LandingPad.Medium),
("Federation_Dropship", "Federal Dropship", LandingPad.Medium),
("Type7", "Type-7 Transporter", LandingPad.Large),
("TypeX", "Alliance Chieftain", LandingPad.Medium),
("Federation_Dropship_MkII", "Federal Assault Ship", LandingPad.Medium),
("Empire_Trader", "Imperial Clipper", LandingPad.Large),
("TypeX_2", "Alliance Crusader", LandingPad.Medium),
("TypeX_3", "Alliance Challenger", LandingPad.Medium),
("Federation_Gunship", "Federal Gunship", LandingPad.Medium),
("Krait_Light", "Krait Phantom", LandingPad.Medium),
("Krait_MkII", "Krait Mk II", LandingPad.Medium),
("Orca", "Orca", LandingPad.Large),
("FerDeLance", "Fer-de-Lance", LandingPad.Medium),
("Mamba", "Mamba", LandingPad.Medium),
("Python", "Python", LandingPad.Medium),
("Type9", "Type-9 Heavy", LandingPad.Large),
("BelugaLiner", "Beluga Liner", LandingPad.Large),
("Type9_Military", "Type-10 Defender", LandingPad.Large),
("Anaconda", "Anaconda", LandingPad.Large),
("Federation_Corvette", "Federal Corvette", LandingPad.Large),
("Cutter", "Imperial Cutter", LandingPad.Large),
("Python_NX", "Python Mk II", LandingPad.Medium),
("Type8", "Type-8 Transporter", LandingPad.Medium),
("Mandalay", "Mandalay", LandingPad.Medium),
("CobraMkV", "Cobra Mk V", LandingPad.Small),
("Corsair", "Corsair", LandingPad.Medium),
("PantherMkII", "Panther Clipper Mk II", LandingPad.Large),
("LakonMiner", "Type-11 Prospector", LandingPad.Medium),
("Explorer_NX", "Caspian Explorer", LandingPad.Large),
("SmallCombat01_NX", "Kestrel Mk II", LandingPad.Small),
("MediumTransport01", "Lynx Highliner", LandingPad.Medium),
("AnacondaMkII", "Anaconda Mk II", LandingPad.Large)
];
var result = new Dictionary<string, LandingPad>(StringComparer.Ordinal);
foreach (var ship in ships)
{
result[Normalize(ship.Type)] = ship.Pad;
result[Normalize(ship.Name)] = ship.Pad;
}
return result;
}
private static string Normalize(string? value) =>
string.Concat((value ?? "").Where(char.IsLetterOrDigit)).ToLowerInvariant();
}
using XFEEDToolBox.Core.Models;
namespace XFEEDToolBox.Core.Planning;
public static class ShipPadPolicy
{
public static void RequireFits(Station station,string actualShipType,LandingPad manual=LandingPad.Unknown)
{
var actual=RequiredPad(actualShipType);
if(actual==LandingPad.Unknown)throw new InvalidOperationException("实际船型尺寸尚未确认,已停止执行。");
var required=(LandingPad)Math.Max((int)manual,(int)actual);var available=StationSourcePolicy.EffectiveMaxLandingPad(station);
if(available==LandingPad.Unknown||available<required)throw new InvalidOperationException($"{station.DisplayName} 的停机坪未确认能容纳当前飞船,已停止执行。");
}
// Derived from the 49 fdname/name/class entries in the bundled Outfitting/Edsy/eddb.js.
// Pinned EDSY commit: 1fafc5d9f2d07e2e92443657c83d0c65b897bb00 (2026-10-20).
// Source SHA-256: e1eb8d9f28745a8a745fa473bd1ccbb4b636aaedcd74cc23cfad9484086873d5.
// edsy.js renders ship.class with '?SML'; this is the hull size, not a module class.
private static readonly IReadOnlyDictionary<string, LandingPad> Pads = CreatePads();
public static LandingPad RequiredPad(string? actualShipType) =>
Pads.TryGetValue(Normalize(actualShipType), out var pad) ? pad : LandingPad.Unknown;
// A manual preference or advisory value can increase the requirement, never reduce
// the verified hull requirement. Callers must reject an unresolved actual hull when
// verified ship compatibility is mandatory; Unknown is not evidence of a small hull.
public static LandingPad EffectiveRequiredPad(LandingPad manual, LandingPad advisory, string? actualShipType) =>
(LandingPad)Math.Max(Math.Max((int)manual, (int)advisory), (int)RequiredPad(actualShipType));
private static IReadOnlyDictionary<string, LandingPad> CreatePads()
{
(string Type, string Name, LandingPad Pad)[] ships =
[
("SideWinder", "Sidewinder", LandingPad.Small),
("Eagle", "Eagle", LandingPad.Small),
("Hauler", "Hauler", LandingPad.Small),
("Adder", "Adder", LandingPad.Small),
("Empire_Eagle", "Imperial Eagle", LandingPad.Small),
("Viper", "Viper Mk III", LandingPad.Small),
("CobraMkIII", "Cobra Mk III", LandingPad.Small),
("Viper_MkIV", "Viper Mk IV", LandingPad.Small),
("DiamondBack", "Diamondback Scout", LandingPad.Small),
("CobraMkIV", "Cobra Mk IV", LandingPad.Small),
("Type6", "Type-6 Transporter", LandingPad.Medium),
("Dolphin", "Dolphin", LandingPad.Small),
("DiamondBackXL", "Diamondback Explorer", LandingPad.Small),
("Empire_Courier", "Imperial Courier", LandingPad.Small),
("Independant_Trader", "Keelback", LandingPad.Medium),
("Asp_Scout", "Asp Scout", LandingPad.Medium),
("Vulture", "Vulture", LandingPad.Small),
("Asp", "Asp Explorer", LandingPad.Medium),
("Federation_Dropship", "Federal Dropship", LandingPad.Medium),
("Type7", "Type-7 Transporter", LandingPad.Large),
("TypeX", "Alliance Chieftain", LandingPad.Medium),
("Federation_Dropship_MkII", "Federal Assault Ship", LandingPad.Medium),
("Empire_Trader", "Imperial Clipper", LandingPad.Large),
("TypeX_2", "Alliance Crusader", LandingPad.Medium),
("TypeX_3", "Alliance Challenger", LandingPad.Medium),
("Federation_Gunship", "Federal Gunship", LandingPad.Medium),
("Krait_Light", "Krait Phantom", LandingPad.Medium),
("Krait_MkII", "Krait Mk II", LandingPad.Medium),
("Orca", "Orca", LandingPad.Large),
("FerDeLance", "Fer-de-Lance", LandingPad.Medium),
("Mamba", "Mamba", LandingPad.Medium),
("Python", "Python", LandingPad.Medium),
("Type9", "Type-9 Heavy", LandingPad.Large),
("BelugaLiner", "Beluga Liner", LandingPad.Large),
("Type9_Military", "Type-10 Defender", LandingPad.Large),
("Anaconda", "Anaconda", LandingPad.Large),
("Federation_Corvette", "Federal Corvette", LandingPad.Large),
("Cutter", "Imperial Cutter", LandingPad.Large),
("Python_NX", "Python Mk II", LandingPad.Medium),
("Type8", "Type-8 Transporter", LandingPad.Medium),
("Mandalay", "Mandalay", LandingPad.Medium),
("CobraMkV", "Cobra Mk V", LandingPad.Small),
("Corsair", "Corsair", LandingPad.Medium),
("PantherMkII", "Panther Clipper Mk II", LandingPad.Large),
("LakonMiner", "Type-11 Prospector", LandingPad.Medium),
("Explorer_NX", "Caspian Explorer", LandingPad.Large),
("SmallCombat01_NX", "Kestrel Mk II", LandingPad.Small),
("MediumTransport01", "Lynx Highliner", LandingPad.Medium),
("AnacondaMkII", "Anaconda Mk II", LandingPad.Large)
];
var result = new Dictionary<string, LandingPad>(StringComparer.Ordinal);
foreach (var ship in ships)
{
result[Normalize(ship.Type)] = ship.Pad;
result[Normalize(ship.Name)] = ship.Pad;
}
return result;
}
private static string Normalize(string? value) =>
string.Concat((value ?? "").Where(char.IsLetterOrDigit)).ToLowerInvariant();
}