Refactor runtime bootstrap and ship control flows
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using SpaceGame.Api.Industry.Planning;
|
||||
using SpaceGame.Api.Stations.Simulation;
|
||||
using static SpaceGame.Api.Shared.Runtime.ShipBehaviorKinds;
|
||||
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;
|
||||
|
||||
namespace SpaceGame.Api.Factions.AI;
|
||||
@@ -13,8 +14,12 @@ internal sealed class CommanderPlanningService
|
||||
private const int MaxDecisionLogEntries = 40;
|
||||
private const int MaxOutcomeEntries = 32;
|
||||
private const int MaxAiOrdersPerShip = 2;
|
||||
private const string MilitaryShipCategory = "military";
|
||||
private const string MiningShipCategory = "mining";
|
||||
private const string TransportShipCategory = "transport";
|
||||
private const string ConstructionShipCategory = "construction";
|
||||
|
||||
internal void UpdateCommanders(SimulationWorld world, float deltaSeconds, ICollection<SimulationEventRecord> events)
|
||||
internal void UpdateCommanders(SimulationWorld world, IPlayerStateStore playerStateStore, float deltaSeconds, ICollection<SimulationEventRecord> events)
|
||||
{
|
||||
EnsureHierarchy(world);
|
||||
|
||||
@@ -33,7 +38,7 @@ internal sealed class CommanderPlanningService
|
||||
|
||||
foreach (var commander in world.Commanders.Where(candidate => candidate.IsAlive && candidate.Kind == CommanderKind.Faction).ToList())
|
||||
{
|
||||
if (PlayerFactionService.IsPlayerFaction(world, commander.FactionId))
|
||||
if (PlayerFactionService.IsPlayerFaction(playerStateStore, commander.FactionId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -48,7 +53,7 @@ internal sealed class CommanderPlanningService
|
||||
|
||||
foreach (var commander in world.Commanders.Where(candidate => candidate.IsAlive && candidate.Kind == CommanderKind.Fleet).ToList())
|
||||
{
|
||||
if (PlayerFactionService.IsPlayerFaction(world, commander.FactionId))
|
||||
if (PlayerFactionService.IsPlayerFaction(playerStateStore, commander.FactionId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -63,7 +68,7 @@ internal sealed class CommanderPlanningService
|
||||
|
||||
foreach (var commander in world.Commanders.Where(candidate => candidate.IsAlive && candidate.Kind == CommanderKind.Station).ToList())
|
||||
{
|
||||
if (PlayerFactionService.IsPlayerFaction(world, commander.FactionId))
|
||||
if (PlayerFactionService.IsPlayerFaction(playerStateStore, commander.FactionId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -78,7 +83,7 @@ internal sealed class CommanderPlanningService
|
||||
|
||||
foreach (var commander in world.Commanders.Where(candidate => candidate.IsAlive && candidate.Kind == CommanderKind.Ship).ToList())
|
||||
{
|
||||
if (PlayerFactionService.IsPlayerFaction(world, commander.FactionId))
|
||||
if (PlayerFactionService.IsPlayerFaction(playerStateStore, commander.FactionId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -268,7 +273,7 @@ internal sealed class CommanderPlanningService
|
||||
CommanderRuntime factionCommander,
|
||||
IReadOnlyDictionary<string, CommanderRuntime> stationCommanders)
|
||||
{
|
||||
if (string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal))
|
||||
if (IsMilitaryShip(ship.Definition))
|
||||
{
|
||||
return factionCommander;
|
||||
}
|
||||
@@ -456,8 +461,8 @@ internal sealed class CommanderPlanningService
|
||||
ship.Id,
|
||||
nextAssignment is null ? "assignment-cleared" : "assignment-updated",
|
||||
nextAssignment is null
|
||||
? $"{ship.Definition.Label} returned to default behavior."
|
||||
: $"{ship.Definition.Label} assigned to {nextAssignment.Kind}.",
|
||||
? $"{ship.Definition.Name} returned to default behavior."
|
||||
: $"{ship.Definition.Name} assigned to {nextAssignment.Kind}.",
|
||||
DateTimeOffset.UtcNow));
|
||||
}
|
||||
}
|
||||
@@ -586,10 +591,10 @@ internal sealed class CommanderPlanningService
|
||||
var frontCount = Math.Max(1,
|
||||
threatAssessment.ThreatSignals.Count(signal => signal.ScopeKind is "controlled-system" or "contested-system")
|
||||
+ (expansionProject is null ? 0 : 1));
|
||||
var militaryShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && ship.Definition.Kind == "military");
|
||||
var minerShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && HasShipCapabilities(ship.Definition, "mining"));
|
||||
var transportShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && ship.Definition.Kind == "transport");
|
||||
var constructorShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && ship.Definition.Kind == "construction");
|
||||
var militaryShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && IsMilitaryShip(ship.Definition));
|
||||
var minerShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && IsMiningShip(ship.Definition));
|
||||
var transportShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && IsTransportShip(ship.Definition));
|
||||
var constructorShipCount = world.Ships.Count(ship => ship.FactionId == faction.Id && ship.Health > 0f && IsConstructionShip(ship.Definition));
|
||||
var hasShipyard = world.Stations.Any(station =>
|
||||
string.Equals(station.FactionId, faction.Id, StringComparison.Ordinal) &&
|
||||
station.InstalledModules.Contains("module_gen_build_l_01", StringComparer.Ordinal));
|
||||
@@ -1365,9 +1370,9 @@ internal sealed class CommanderPlanningService
|
||||
Id = $"{campaign.Id}-protect-station-{station.Id}",
|
||||
CampaignId = campaign.Id,
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "protect-station",
|
||||
Kind = ProtectStation,
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "protect-station",
|
||||
BehaviorKind = ProtectStation,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 8f,
|
||||
HomeSystemId = station.SystemId,
|
||||
@@ -1389,7 +1394,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "patrol-front",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "patrol",
|
||||
BehaviorKind = Patrol,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 2f,
|
||||
HomeSystemId = campaign.TargetSystemId,
|
||||
@@ -1414,7 +1419,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "police-front",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "police",
|
||||
BehaviorKind = Police,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 1f,
|
||||
HomeSystemId = campaign.TargetSystemId,
|
||||
@@ -1454,7 +1459,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "strike-station",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "attack-target",
|
||||
BehaviorKind = AttackTarget,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 10f,
|
||||
TargetSystemId = enemyStation.SystemId,
|
||||
@@ -1478,7 +1483,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "hold-front",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "protect-position",
|
||||
BehaviorKind = ProtectPosition,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 3f,
|
||||
TargetSystemId = campaign.TargetSystemId,
|
||||
@@ -1500,7 +1505,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "fleet-sustainment",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "supply-fleet",
|
||||
BehaviorKind = SupplyFleet,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 1.5f,
|
||||
HomeSystemId = campaign.TargetSystemId,
|
||||
@@ -1539,7 +1544,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "construct-site",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "construct-station",
|
||||
BehaviorKind = ConstructStation,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 8f,
|
||||
HomeSystemId = expansionProject.SystemId,
|
||||
@@ -1564,7 +1569,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "supply-site",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "find-build-tasks",
|
||||
BehaviorKind = FindBuildTasks,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 4f,
|
||||
HomeSystemId = expansionProject.SystemId,
|
||||
@@ -1589,7 +1594,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "guard-site",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "protect-position",
|
||||
BehaviorKind = ProtectPosition,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 2f,
|
||||
TargetSystemId = expansionProject.SystemId,
|
||||
@@ -1614,7 +1619,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "mine-expansion-input",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "expert-auto-mine",
|
||||
BehaviorKind = ExpertAutoMine,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 1f,
|
||||
HomeSystemId = expansionProject.SystemId,
|
||||
@@ -1655,7 +1660,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "trade-shortage",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "fill-shortages",
|
||||
BehaviorKind = FillShortages,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 5f,
|
||||
HomeSystemId = anchorStation?.SystemId,
|
||||
@@ -1680,7 +1685,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "mine-shortage",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "expert-auto-mine",
|
||||
BehaviorKind = ExpertAutoMine,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 3f,
|
||||
HomeSystemId = anchorStation?.SystemId,
|
||||
@@ -1703,7 +1708,7 @@ internal sealed class CommanderPlanningService
|
||||
TheaterId = theater?.Id,
|
||||
Kind = "revisit-stations",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "revisit-known-stations",
|
||||
BehaviorKind = RevisitKnownStations,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 0.5f,
|
||||
HomeSystemId = anchorStation?.SystemId,
|
||||
@@ -1743,7 +1748,7 @@ internal sealed class CommanderPlanningService
|
||||
CampaignId = campaign.Id,
|
||||
Kind = "feed-shipyard",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "fill-shortages",
|
||||
BehaviorKind = FillShortages,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 4f,
|
||||
HomeSystemId = shipyard.SystemId,
|
||||
@@ -1768,7 +1773,7 @@ internal sealed class CommanderPlanningService
|
||||
CampaignId = campaign.Id,
|
||||
Kind = "mine-bottleneck",
|
||||
DelegationKind = "ship",
|
||||
BehaviorKind = "expert-auto-mine",
|
||||
BehaviorKind = ExpertAutoMine,
|
||||
Status = "active",
|
||||
Priority = campaign.Priority + 2f,
|
||||
HomeSystemId = shipyard.SystemId,
|
||||
@@ -1838,7 +1843,9 @@ internal sealed class CommanderPlanningService
|
||||
var reservedCommanderIds = new HashSet<string>(StringComparer.Ordinal);
|
||||
var availableMilitaryCommanders = commanders.Count(commander =>
|
||||
commander.Kind == CommanderKind.Ship &&
|
||||
world.Ships.FirstOrDefault(ship => ship.Id == commander.ControlledEntityId) is { Definition.Kind: "military", Health: > 0f });
|
||||
world.Ships.FirstOrDefault(ship => ship.Id == commander.ControlledEntityId) is { } commanderShip
|
||||
&& commanderShip.Health > 0f
|
||||
&& IsMilitaryShip(commanderShip.Definition));
|
||||
var committedMilitaryCommanders = 0;
|
||||
|
||||
foreach (var objective in objectives
|
||||
@@ -1921,11 +1928,11 @@ internal sealed class CommanderPlanningService
|
||||
|
||||
return objective.BehaviorKind switch
|
||||
{
|
||||
"construct-station" => string.Equals(ship.Definition.Kind, "construction", StringComparison.Ordinal),
|
||||
"find-build-tasks" => string.Equals(ship.Definition.Kind, "transport", StringComparison.Ordinal),
|
||||
"fill-shortages" or "advanced-auto-trade" or "revisit-known-stations" or "supply-fleet" => string.Equals(ship.Definition.Kind, "transport", StringComparison.Ordinal),
|
||||
"local-auto-mine" or "advanced-auto-mine" or "expert-auto-mine" => HasShipCapabilities(ship.Definition, "mining"),
|
||||
"patrol" or "police" or "protect-position" or "protect-ship" or "protect-station" or "attack-target" => string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal),
|
||||
ConstructStation => IsConstructionShip(ship.Definition),
|
||||
FindBuildTasks => IsTransportShip(ship.Definition),
|
||||
FillShortages or AdvancedAutoTrade or RevisitKnownStations or SupplyFleet => IsTransportShip(ship.Definition),
|
||||
LocalAutoMine or AdvancedAutoMine or ExpertAutoMine => IsMiningShip(ship.Definition),
|
||||
Patrol or Police or ProtectPosition or ProtectShip or ProtectStation or AttackTarget => IsMilitaryShip(ship.Definition),
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
@@ -1992,7 +1999,7 @@ internal sealed class CommanderPlanningService
|
||||
Kind = "military-fleet",
|
||||
Status = economicAssessment.MilitaryShipCount >= economicAssessment.TargetMilitaryShipCount ? "stable" : "active",
|
||||
Priority = 80f + (threatAssessment.ThreatSignals.Count * 4f),
|
||||
ShipKind = "military",
|
||||
ShipKind = MilitaryShipCategory,
|
||||
TargetCount = economicAssessment.TargetMilitaryShipCount,
|
||||
CurrentCount = economicAssessment.MilitaryShipCount,
|
||||
Notes = "Maintain enough military hulls for all active fronts.",
|
||||
@@ -2004,7 +2011,7 @@ internal sealed class CommanderPlanningService
|
||||
Kind = "mining-fleet",
|
||||
Status = economicAssessment.MinerShipCount >= economicAssessment.TargetMinerShipCount ? "stable" : "active",
|
||||
Priority = 60f,
|
||||
ShipKind = "mining",
|
||||
ShipKind = MiningShipCategory,
|
||||
TargetCount = economicAssessment.TargetMinerShipCount,
|
||||
CurrentCount = economicAssessment.MinerShipCount,
|
||||
Notes = "Maintain raw resource extraction capacity.",
|
||||
@@ -2016,7 +2023,7 @@ internal sealed class CommanderPlanningService
|
||||
Kind = "logistics-fleet",
|
||||
Status = economicAssessment.TransportShipCount >= economicAssessment.TargetTransportShipCount ? "stable" : "active",
|
||||
Priority = 62f,
|
||||
ShipKind = "transport",
|
||||
ShipKind = TransportShipCategory,
|
||||
TargetCount = economicAssessment.TargetTransportShipCount,
|
||||
CurrentCount = economicAssessment.TransportShipCount,
|
||||
Notes = "Maintain logistics throughput across stations and fronts.",
|
||||
@@ -2028,7 +2035,7 @@ internal sealed class CommanderPlanningService
|
||||
Kind = "construction-fleet",
|
||||
Status = economicAssessment.ConstructorShipCount >= economicAssessment.TargetConstructorShipCount ? "stable" : "active",
|
||||
Priority = expansionProject is null ? 35f : 68f,
|
||||
ShipKind = "construction",
|
||||
ShipKind = ConstructionShipCategory,
|
||||
TargetCount = economicAssessment.TargetConstructorShipCount,
|
||||
CurrentCount = economicAssessment.ConstructorShipCount,
|
||||
Notes = "Maintain construction capacity for frontier growth.",
|
||||
@@ -2347,10 +2354,10 @@ internal sealed class CommanderPlanningService
|
||||
Kind = "fleet-command",
|
||||
BehaviorKind = campaign.Kind switch
|
||||
{
|
||||
"offense" => "attack-target",
|
||||
"defense" => "protect-position",
|
||||
"expansion" => "protect-position",
|
||||
_ => "patrol",
|
||||
"offense" => AttackTarget,
|
||||
"defense" => ProtectPosition,
|
||||
"expansion" => ProtectPosition,
|
||||
_ => Patrol,
|
||||
},
|
||||
Status = campaign.Status,
|
||||
Priority = campaign.Priority,
|
||||
@@ -2380,7 +2387,7 @@ internal sealed class CommanderPlanningService
|
||||
{
|
||||
ObjectiveId = $"objective-station-{station.Id}-ship-production",
|
||||
Kind = "ship-production-focus",
|
||||
BehaviorKind = "fill-shortages",
|
||||
BehaviorKind = FillShortages,
|
||||
Status = "active",
|
||||
Priority = 55f,
|
||||
HomeSystemId = station.SystemId,
|
||||
@@ -2399,7 +2406,7 @@ internal sealed class CommanderPlanningService
|
||||
{
|
||||
ObjectiveId = $"objective-station-{station.Id}-commodity-focus-{bottleneckItem}",
|
||||
Kind = "commodity-focus",
|
||||
BehaviorKind = "fill-shortages",
|
||||
BehaviorKind = FillShortages,
|
||||
Status = "active",
|
||||
Priority = 45f,
|
||||
HomeSystemId = station.SystemId,
|
||||
@@ -2418,7 +2425,7 @@ internal sealed class CommanderPlanningService
|
||||
{
|
||||
ObjectiveId = $"objective-station-{station.Id}-expansion-support",
|
||||
Kind = "expansion-support",
|
||||
BehaviorKind = "find-build-tasks",
|
||||
BehaviorKind = FindBuildTasks,
|
||||
Status = "active",
|
||||
Priority = 40f,
|
||||
HomeSystemId = station.SystemId,
|
||||
@@ -2435,7 +2442,7 @@ internal sealed class CommanderPlanningService
|
||||
{
|
||||
ObjectiveId = $"objective-station-{station.Id}-oversight",
|
||||
Kind = "station-oversight",
|
||||
BehaviorKind = "fill-shortages",
|
||||
BehaviorKind = FillShortages,
|
||||
Status = "active",
|
||||
Priority = 30f,
|
||||
HomeSystemId = station.SystemId,
|
||||
@@ -2460,7 +2467,7 @@ internal sealed class CommanderPlanningService
|
||||
faction.StrategicState.Objectives.Any(objective =>
|
||||
objective.CampaignId == campaign.Id &&
|
||||
objective.CommanderId is not null &&
|
||||
(IsCombatObjective(objective) || string.Equals(objective.BehaviorKind, "supply-fleet", StringComparison.Ordinal))))
|
||||
(IsCombatObjective(objective) || string.Equals(objective.BehaviorKind, SupplyFleet, StringComparison.Ordinal))))
|
||||
.Select(campaign => campaign.Id)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
|
||||
@@ -2510,10 +2517,10 @@ internal sealed class CommanderPlanningService
|
||||
Kind = "fleet-command",
|
||||
BehaviorKind = campaign.Kind switch
|
||||
{
|
||||
"offense" => "attack-target",
|
||||
"defense" => "protect-position",
|
||||
"expansion" => "protect-position",
|
||||
_ => "patrol",
|
||||
"offense" => AttackTarget,
|
||||
"defense" => ProtectPosition,
|
||||
"expansion" => ProtectPosition,
|
||||
_ => Patrol,
|
||||
},
|
||||
Status = campaign.Status,
|
||||
Priority = campaign.Priority + 1f,
|
||||
@@ -2581,7 +2588,7 @@ internal sealed class CommanderPlanningService
|
||||
{
|
||||
if (objective?.CampaignId is not null
|
||||
&& fleetCommanders.TryGetValue(objective.CampaignId, out var fleetCommander)
|
||||
&& (IsCombatObjective(objective) || string.Equals(objective.BehaviorKind, "supply-fleet", StringComparison.Ordinal)))
|
||||
&& (IsCombatObjective(objective) || string.Equals(objective.BehaviorKind, SupplyFleet, StringComparison.Ordinal)))
|
||||
{
|
||||
return fleetCommander.Id;
|
||||
}
|
||||
@@ -2598,25 +2605,39 @@ internal sealed class CommanderPlanningService
|
||||
private static DefaultBehaviorRuntime BuildFallbackBehavior(SimulationWorld world, ShipRuntime ship)
|
||||
{
|
||||
var homeStation = ResolveFallbackHomeStation(world, ship);
|
||||
if (HasShipCapabilities(ship.Definition, "mining"))
|
||||
if (IsMiningShip(ship.Definition))
|
||||
{
|
||||
if (homeStation is null)
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = LocalAutoMine,
|
||||
HomeSystemId = ship.SystemId,
|
||||
HomeStationId = null,
|
||||
AreaSystemId = ship.SystemId,
|
||||
ItemId = "ore",
|
||||
Radius = 24f,
|
||||
MaxSystemRange = 0,
|
||||
};
|
||||
}
|
||||
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = ship.Definition.CargoCapacity >= 120f ? "expert-auto-mine" : "advanced-auto-mine",
|
||||
Kind = ship.Definition.GetTotalCargoCapacity() >= 120f ? ExpertAutoMine : AdvancedAutoMine,
|
||||
HomeSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
AreaSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
PreferredItemId = null,
|
||||
ItemId = null,
|
||||
Radius = 24f,
|
||||
MaxSystemRange = ship.Definition.CargoCapacity >= 120f ? 3 : 1,
|
||||
MaxSystemRange = ship.Definition.GetTotalCargoCapacity() >= 120f ? 3 : 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (string.Equals(ship.Definition.Kind, "transport", StringComparison.Ordinal))
|
||||
if (IsTransportShip(ship.Definition))
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "advanced-auto-trade",
|
||||
Kind = AdvancedAutoTrade,
|
||||
HomeSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
AreaSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
@@ -2625,11 +2646,11 @@ internal sealed class CommanderPlanningService
|
||||
};
|
||||
}
|
||||
|
||||
if (string.Equals(ship.Definition.Kind, "construction", StringComparison.Ordinal))
|
||||
if (IsConstructionShip(ship.Definition))
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "construct-station",
|
||||
Kind = ConstructStation,
|
||||
HomeSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
AreaSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
@@ -2638,13 +2659,13 @@ internal sealed class CommanderPlanningService
|
||||
};
|
||||
}
|
||||
|
||||
if (string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal))
|
||||
if (IsMilitaryShip(ship.Definition))
|
||||
{
|
||||
var anchor = homeStation?.Position ?? ship.Position;
|
||||
var patrolRadius = (homeStation?.Radius ?? 30f) + 90f;
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "patrol",
|
||||
Kind = Patrol,
|
||||
HomeSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
AreaSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
@@ -2660,7 +2681,7 @@ internal sealed class CommanderPlanningService
|
||||
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "idle",
|
||||
Kind = Idle,
|
||||
HomeSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
AreaSystemId = homeStation?.SystemId ?? ship.SystemId,
|
||||
@@ -2684,15 +2705,15 @@ internal sealed class CommanderPlanningService
|
||||
var areaSystemId = objective.TargetSystemId ?? objective.HomeSystemId ?? fallback.AreaSystemId ?? ship.SystemId;
|
||||
var radius = objective.BehaviorKind switch
|
||||
{
|
||||
"protect-position" or "protect-station" or "patrol" or "police" => MathF.Max(80f, fallback.Radius),
|
||||
"follow-ship" or "protect-ship" => MathF.Max(18f, fallback.Radius * 0.6f),
|
||||
"fill-shortages" or "advanced-auto-trade" or "find-build-tasks" => MathF.Max(20f, fallback.Radius),
|
||||
ProtectPosition or ProtectStation or Patrol or Police => MathF.Max(80f, fallback.Radius),
|
||||
FollowShip or ProtectShip => MathF.Max(18f, fallback.Radius * 0.6f),
|
||||
FillShortages or AdvancedAutoTrade or FindBuildTasks => MathF.Max(20f, fallback.Radius),
|
||||
_ => fallback.Radius,
|
||||
};
|
||||
var maxRange = objective.BehaviorKind switch
|
||||
{
|
||||
"attack-target" or "protect-position" or "protect-station" or "protect-ship" or "patrol" or "police" => Math.Max(1, fallback.MaxSystemRange),
|
||||
"fill-shortages" or "advanced-auto-trade" or "find-build-tasks" or "supply-fleet" => Math.Max(2, fallback.MaxSystemRange),
|
||||
AttackTarget or ProtectPosition or ProtectStation or ProtectShip or Patrol or Police => Math.Max(1, fallback.MaxSystemRange),
|
||||
FillShortages or AdvancedAutoTrade or FindBuildTasks or SupplyFleet => Math.Max(2, fallback.MaxSystemRange),
|
||||
_ => fallback.MaxSystemRange,
|
||||
};
|
||||
|
||||
@@ -2703,16 +2724,16 @@ internal sealed class CommanderPlanningService
|
||||
HomeStationId = objective.HomeStationId ?? fallback.HomeStationId,
|
||||
AreaSystemId = areaSystemId,
|
||||
TargetEntityId = objective.TargetEntityId,
|
||||
PreferredItemId = objective.ItemId ?? fallback.PreferredItemId,
|
||||
ItemId = objective.ItemId ?? fallback.ItemId,
|
||||
PreferredNodeId = fallback.PreferredNodeId,
|
||||
PreferredConstructionSiteId = objective.Kind is "construct-site" or "supply-site" ? objective.TargetEntityId : fallback.PreferredConstructionSiteId,
|
||||
PreferredModuleId = fallback.PreferredModuleId,
|
||||
TargetPosition = objective.TargetPosition ?? fallback.TargetPosition,
|
||||
WaitSeconds = objective.BehaviorKind == "supply-fleet" ? 4f : fallback.WaitSeconds,
|
||||
WaitSeconds = objective.BehaviorKind == SupplyFleet ? 4f : fallback.WaitSeconds,
|
||||
Radius = radius,
|
||||
MaxSystemRange = maxRange,
|
||||
KnownStationsOnly = objective.BehaviorKind == "revisit-known-stations",
|
||||
PatrolPoints = objective.BehaviorKind == "patrol"
|
||||
KnownStationsOnly = objective.BehaviorKind == RevisitKnownStations,
|
||||
PatrolPoints = objective.BehaviorKind == Patrol
|
||||
? BuildPatrolPoints(objective.TargetPosition ?? fallback.TargetPosition ?? ship.Position, radius)
|
||||
: [],
|
||||
PatrolIndex = ship.DefaultBehavior.PatrolIndex,
|
||||
@@ -2728,7 +2749,7 @@ internal sealed class CommanderPlanningService
|
||||
target.HomeStationId = source.HomeStationId;
|
||||
target.AreaSystemId = source.AreaSystemId;
|
||||
target.TargetEntityId = source.TargetEntityId;
|
||||
target.PreferredItemId = source.PreferredItemId;
|
||||
target.ItemId = source.ItemId;
|
||||
target.PreferredNodeId = source.PreferredNodeId;
|
||||
target.PreferredConstructionSiteId = source.PreferredConstructionSiteId;
|
||||
target.PreferredModuleId = source.PreferredModuleId;
|
||||
@@ -2749,7 +2770,7 @@ internal sealed class CommanderPlanningService
|
||||
&& string.Equals(left.HomeStationId, right.HomeStationId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.AreaSystemId, right.AreaSystemId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.TargetEntityId, right.TargetEntityId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.PreferredItemId, right.PreferredItemId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.ItemId, right.ItemId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.PreferredNodeId, right.PreferredNodeId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.PreferredConstructionSiteId, right.PreferredConstructionSiteId, StringComparison.Ordinal)
|
||||
&& string.Equals(left.PreferredModuleId, right.PreferredModuleId, StringComparison.Ordinal)
|
||||
@@ -2805,13 +2826,15 @@ internal sealed class CommanderPlanningService
|
||||
{
|
||||
Id = $"ai-order-{objective.Id}",
|
||||
Kind = objective.StagingOrderKind,
|
||||
SourceKind = ShipOrderSourceKind.Commander,
|
||||
SourceId = objective.Id,
|
||||
Priority = 90 + objective.ReinforcementLevel,
|
||||
InterruptCurrentPlan = true,
|
||||
Label = $"{objective.Kind} staging",
|
||||
TargetEntityId = objective.TargetEntityId,
|
||||
TargetSystemId = targetSystemId,
|
||||
TargetPosition = targetPosition,
|
||||
DestinationStationId = objective.BehaviorKind == "dock-and-wait" ? objective.TargetEntityId : null,
|
||||
DestinationStationId = objective.BehaviorKind == DockAndWait ? objective.TargetEntityId : null,
|
||||
ItemId = objective.ItemId,
|
||||
WaitSeconds = 0f,
|
||||
Radius = MathF.Max(12f, objective.ReinforcementLevel * 18f),
|
||||
@@ -2885,6 +2908,8 @@ internal sealed class CommanderPlanningService
|
||||
private static bool ShipOrdersEqual(ShipOrderRuntime left, ShipOrderRuntime right) =>
|
||||
string.Equals(left.Id, right.Id, StringComparison.Ordinal)
|
||||
&& string.Equals(left.Kind, right.Kind, StringComparison.Ordinal)
|
||||
&& left.SourceKind == right.SourceKind
|
||||
&& string.Equals(left.SourceId, right.SourceId, StringComparison.Ordinal)
|
||||
&& left.Priority == right.Priority
|
||||
&& left.InterruptCurrentPlan == right.InterruptCurrentPlan
|
||||
&& string.Equals(left.Label, right.Label, StringComparison.Ordinal)
|
||||
@@ -2920,7 +2945,7 @@ internal sealed class CommanderPlanningService
|
||||
}
|
||||
|
||||
private static bool IsCombatObjective(FactionOperationalObjectiveRuntime objective) =>
|
||||
objective.BehaviorKind is "attack-target" or "protect-position" or "protect-ship" or "protect-station" or "patrol" or "police";
|
||||
objective.BehaviorKind is AttackTarget or ProtectPosition or ProtectShip or ProtectStation or Patrol or Police;
|
||||
|
||||
private static float EstimateFriendlyAssetValue(SimulationWorld world, string factionId, string systemId)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user