feat: simplifying the simulation

This commit is contained in:
2026-03-17 16:08:02 -04:00
parent 3234b628ea
commit d5d0a39244
20 changed files with 374 additions and 496 deletions

View File

@@ -3,8 +3,8 @@ namespace SpaceGame.Simulation.Api.Contracts;
public sealed record ShipSnapshot(
string Id,
string Label,
string Role,
string ShipClass,
string Kind,
string Class,
string SystemId,
Vector3Dto LocalPosition,
Vector3Dto LocalVelocity,
@@ -19,8 +19,7 @@ public sealed record ShipSnapshot(
string? CommanderId,
string? PolicySetId,
float CargoCapacity,
string? CargoItemId,
float WorkerPopulation,
float TravelSpeed,
string TravelSpeedUnit,
IReadOnlyList<InventoryEntry> Inventory,
@@ -33,8 +32,8 @@ public sealed record ShipSnapshot(
public sealed record ShipDelta(
string Id,
string Label,
string Role,
string ShipClass,
string Kind,
string Class,
string SystemId,
Vector3Dto LocalPosition,
Vector3Dto LocalVelocity,
@@ -49,8 +48,7 @@ public sealed record ShipDelta(
string? CommanderId,
string? PolicySetId,
float CargoCapacity,
string? CargoItemId,
float WorkerPopulation,
float TravelSpeed,
string TravelSpeedUnit,
IReadOnlyList<InventoryEntry> Inventory,

View File

@@ -147,20 +147,19 @@ public sealed class ShipDefinition
{
public required string Id { get; set; }
public required string Label { get; set; }
public required string Role { get; set; }
public required string ShipClass { get; set; }
public required string Kind { get; set; }
public required string Class { get; set; }
public float Speed { get; set; }
public float WarpSpeed { get; set; }
public float FtlSpeed { get; set; }
public float SpoolTime { get; set; }
public float CargoCapacity { get; set; }
public string? CargoKind { get; set; }
public string? CargoItemId { get; set; }
public required string Color { get; set; }
public required string HullColor { get; set; }
public float Size { get; set; }
public float MaxHealth { get; set; }
public List<string> Modules { get; set; } = [];
public List<string> Capabilities { get; set; } = [];
public ConstructionDefinition? Construction { get; set; }
}

View File

@@ -18,7 +18,7 @@ internal sealed class ShipBehaviorStateMachine
{
idleState,
new PatrolShipBehaviorState(),
new ResourceHarvestShipBehaviorState("auto-mine", "ore", "mining-turret"),
new ResourceHarvestShipBehaviorState("auto-mine", "ore", "mining"),
new ConstructStationShipBehaviorState(),
};

View File

@@ -18,7 +18,7 @@ public sealed class ShipRuntime
public required ControllerTaskRuntime ControllerTask { get; set; }
public float ActionTimer { get; set; }
public Dictionary<string, float> Inventory { get; } = new(StringComparer.Ordinal);
public float WorkerPopulation { get; set; }
public string DockedStationId { get; set; }
public int? AssignedDockingPadIndex { get; set; }
public string? CommanderId { get; set; }
@@ -62,4 +62,5 @@ public sealed class ControllerTaskRuntime
public string? TargetNodeId { get; set; }
public Vector3? TargetPosition { get; set; }
public float Threshold { get; set; }
public string? ItemId { get; set; }
}

View File

@@ -62,8 +62,7 @@ public enum ControllerTaskKind
Unload,
DeliverConstruction,
BuildConstructionSite,
LoadWorkers,
UnloadWorkers,
ConstructModule,
Undock,
}
@@ -105,8 +104,7 @@ public static class ShipTaskKinds
public const string Undock = "undock";
public const string LoadCargo = "load-cargo";
public const string UnloadCargo = "unload-cargo";
public const string LoadWorkers = "load-workers";
public const string UnloadWorkers = "unload-workers";
public const string MineNode = "mine-node";
public const string HarvestGas = "harvest-gas";
public const string DeliverToStation = "deliver-to-station";
@@ -229,8 +227,7 @@ public static class SimulationEnumMappings
ControllerTaskKind.Unload => "unload",
ControllerTaskKind.DeliverConstruction => "deliver-construction",
ControllerTaskKind.BuildConstructionSite => "build-construction-site",
ControllerTaskKind.LoadWorkers => "load-workers",
ControllerTaskKind.UnloadWorkers => "unload-workers",
ControllerTaskKind.ConstructModule => "construct-module",
ControllerTaskKind.Undock => "undock",
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),

View File

@@ -379,7 +379,7 @@ public sealed partial class ScenarioLoader
IReadOnlyDictionary<string, List<Vector3>> patrolRoutes,
StationRuntime? refinery)
{
if (string.Equals(definition.Role, "construction", StringComparison.Ordinal) && refinery is not null)
if (string.Equals(definition.Kind, "construction", StringComparison.Ordinal) && refinery is not null)
{
return new DefaultBehaviorRuntime
{
@@ -389,12 +389,12 @@ public sealed partial class ScenarioLoader
};
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "mining-turret") && refinery is not null)
if (HasCapabilities(definition, "mining") && refinery is not null)
{
return CreateResourceHarvestBehavior("auto-mine", scenario.MiningDefaults.NodeSystemId, refinery.Id);
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "gun-turret") && patrolRoutes.TryGetValue(systemId, out var route))
if (string.Equals(definition.Kind, "military", StringComparison.Ordinal) && patrolRoutes.TryGetValue(systemId, out var route))
{
return new DefaultBehaviorRuntime
{

View File

@@ -400,8 +400,8 @@ public sealed partial class ScenarioLoader
private static bool HasInstalledModules(StationRuntime station, params string[] modules) =>
modules.All((moduleId) => station.Modules.Any((candidate) => string.Equals(candidate.ModuleId, moduleId, StringComparison.Ordinal)));
private static bool HasModules(ShipDefinition definition, params string[] modules) =>
modules.All((moduleId) => definition.Modules.Contains(moduleId, StringComparer.Ordinal));
private static bool HasCapabilities(ShipDefinition definition, params string[] capabilities) =>
capabilities.All((cap) => definition.Capabilities.Contains(cap, StringComparer.Ordinal));
private static void AddStationModule(StationRuntime station, IReadOnlyDictionary<string, ModuleDefinition> moduleDefinitions, string moduleId)
{

View File

@@ -27,8 +27,7 @@ public sealed partial class SimulationEngine
ControllerTaskKind.Unload => UpdateUnload(ship, world, deltaSeconds),
ControllerTaskKind.DeliverConstruction => UpdateDeliverConstruction(ship, world, deltaSeconds),
ControllerTaskKind.BuildConstructionSite => UpdateBuildConstructionSite(ship, world, deltaSeconds),
ControllerTaskKind.LoadWorkers => UpdateLoadWorkers(ship, world, deltaSeconds),
ControllerTaskKind.UnloadWorkers => UpdateUnloadWorkers(ship, world, deltaSeconds),
ControllerTaskKind.ConstructModule => UpdateConstructModule(ship, world, deltaSeconds),
ControllerTaskKind.Undock => UpdateUndock(ship, world, deltaSeconds),
_ => UpdateIdle(ship, world, deltaSeconds),
@@ -58,6 +57,12 @@ public sealed partial class SimulationEngine
if (ship.SystemId != task.TargetSystemId)
{
if (!HasShipCapabilities(ship.Definition, "ftl"))
{
ship.State = ShipState.Idle;
return "none";
}
var destinationEntryNode = ResolveSystemEntryNode(world, task.TargetSystemId);
var destinationEntryPosition = destinationEntryNode?.Position ?? Vector3.Zero;
return UpdateFtlTransit(ship, world, deltaSeconds, task.TargetSystemId, destinationEntryPosition, destinationEntryNode);
@@ -66,6 +71,11 @@ public sealed partial class SimulationEngine
var currentNode = ResolveCurrentNode(world, ship);
if (targetNode is not null && currentNode is not null && !string.Equals(currentNode.Id, targetNode.Id, StringComparison.Ordinal))
{
if (!HasShipCapabilities(ship.Definition, "warp"))
{
return UpdateLocalTravel(ship, world, deltaSeconds, task.TargetSystemId, targetPosition, targetNode, task.Threshold);
}
return UpdateWarpTransit(ship, world, deltaSeconds, targetPosition, targetNode);
}

View File

@@ -5,14 +5,8 @@ namespace SpaceGame.Simulation.Api.Simulation;
public sealed partial class SimulationEngine
{
private static bool HasShipModules(ShipDefinition definition, params string[] modules) =>
modules.All(moduleId => definition.Modules.Contains(moduleId, StringComparer.Ordinal));
private static bool CanTransportWorkers(ShipRuntime ship) =>
CountModules(ship.Definition.Modules, "habitat-ring") > 0;
private static float GetWorkerTransportCapacity(ShipRuntime ship) =>
CountModules(ship.Definition.Modules, "habitat-ring") * 120f;
private static bool HasShipCapabilities(ShipDefinition definition, params string[] capabilities) =>
capabilities.All(cap => definition.Capabilities.Contains(cap, StringComparer.Ordinal));
private static int CountStationModules(StationRuntime station, string moduleId) =>
station.Modules.Count(module => string.Equals(module.ModuleId, moduleId, StringComparison.Ordinal));
@@ -56,8 +50,8 @@ public sealed partial class SimulationEngine
var moduleCapacity = storageClass switch
{
"bulk-solid" => bulkBays * 1000f,
"bulk-liquid" => liquidTanks * 500f,
"solid" => bulkBays * 1000f,
"liquid" => liquidTanks * 500f,
"container" => containerBays * 800f,
"manufactured" => containerBays * 200f,
_ => 0f,
@@ -102,15 +96,13 @@ public sealed partial class SimulationEngine
private static bool HasStationModules(StationRuntime station, params string[] modules) =>
modules.All(moduleId => station.Modules.Any(candidate => string.Equals(candidate.ModuleId, moduleId, StringComparison.Ordinal)));
private static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node) =>
node.ItemId switch
{
"ore" => HasShipModules(ship.Definition, "mining-turret"),
_ => false,
};
private static bool CanExtractNode(ShipRuntime ship, ResourceNodeRuntime node, SimulationWorld world) =>
HasShipCapabilities(ship.Definition, "mining")
&& world.ItemDefinitions.TryGetValue(node.ItemId, out var item)
&& string.Equals(item.CargoKind, ship.Definition.CargoKind, StringComparison.Ordinal);
private static bool CanBuildClaimBeacon(ShipRuntime ship) =>
string.Equals(ship.Definition.Role, "military", StringComparison.Ordinal);
string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal);
private static float ComputeWorkforceRatio(float population, float workforceRequired)
{
@@ -126,8 +118,8 @@ public sealed partial class SimulationEngine
private static string? GetStorageRequirement(string storageClass) =>
storageClass switch
{
"bulk-solid" => "bulk-bay",
"bulk-liquid" => "liquid-tank",
"solid" => "bulk-bay",
"liquid" => "liquid-tank",
_ => null,
};

View File

@@ -143,8 +143,8 @@ public sealed partial class SimulationEngine
world.Ships.Select(ship => ToShipDelta(world, ship)).Select(ship => new ShipSnapshot(
ship.Id,
ship.Label,
ship.Role,
ship.ShipClass,
ship.Kind,
ship.Class,
ship.SystemId,
ship.LocalPosition,
ship.LocalVelocity,
@@ -159,8 +159,6 @@ public sealed partial class SimulationEngine
ship.CommanderId,
ship.PolicySetId,
ship.CargoCapacity,
ship.CargoItemId,
ship.WorkerPopulation,
ship.TravelSpeed,
ship.TravelSpeedUnit,
ship.Inventory,
@@ -482,7 +480,6 @@ public sealed partial class SimulationEngine
ship.DockedStationId ?? "none",
ship.CommanderId ?? "none",
ship.PolicySetId ?? "none",
ship.WorkerPopulation.ToString("0.###"),
ship.SpatialState.SpaceLayer,
ship.SpatialState.CurrentNodeId ?? "none",
ship.SpatialState.CurrentBubbleId ?? "none",
@@ -586,7 +583,7 @@ public sealed partial class SimulationEngine
private static IReadOnlyList<StationStorageUsageSnapshot> ToStationStorageUsageSnapshots(SimulationWorld world, StationRuntime station)
{
string[] storageClasses = ["bulk-solid", "bulk-liquid", "container", "manufactured"];
string[] storageClasses = ["solid", "liquid", "container", "manufactured"];
return storageClasses
.Select(storageClass => new StationStorageUsageSnapshot(
storageClass,
@@ -666,8 +663,8 @@ public sealed partial class SimulationEngine
private ShipDelta ToShipDelta(SimulationWorld world, ShipRuntime ship) => new(
ship.Id,
ship.Definition.Label,
ship.Definition.Role,
ship.Definition.ShipClass,
ship.Definition.Kind,
ship.Definition.Class,
ship.SystemId,
ToDto(ship.Position),
ToDto(ship.Velocity),
@@ -682,8 +679,7 @@ public sealed partial class SimulationEngine
ship.CommanderId,
ship.PolicySetId,
ship.Definition.CargoCapacity,
ship.Definition.CargoItemId,
ship.WorkerPopulation,
ToShipTravelSpeed(ship).Speed,
ToShipTravelSpeed(ship).Unit,
ToInventoryEntries(ship.Inventory),
@@ -705,14 +701,7 @@ public sealed partial class SimulationEngine
ShipState.Docking => CreateShipActionProgress("Docking", ship.ActionTimer, MathF.Max(world.Balance.DockingDuration, 0.1f)),
ShipState.Undocking => CreateShipActionProgress("Undocking", ship.ActionTimer, MathF.Max(world.Balance.UndockingDuration, 0.1f)),
ShipState.Transferring => CreateShipRemainingActionProgress("Transfer", ship.TrackedActionTotal, GetShipCargoAmount(ship)),
ShipState.Loading => CreateShipRemainingActionProgress(
"Load workers",
ship.TrackedActionTotal,
MathF.Max(0f, ship.TrackedActionTotal - ship.WorkerPopulation)),
ShipState.Unloading => CreateShipRemainingActionProgress(
"Unload workers",
ship.TrackedActionTotal,
ship.WorkerPopulation),
ShipState.Loading or ShipState.Unloading => null,
ShipState.DeliveringConstruction => ship.ControllerTask.TargetEntityId is null
? null
: world.ConstructionSites.FirstOrDefault(site => site.Id == ship.ControllerTask.TargetEntityId) is not { } site

View File

@@ -106,10 +106,38 @@ public sealed partial class SimulationEngine
private static string? GetNextStationModuleToBuild(StationRuntime station, SimulationWorld world)
{
// Expand storage before it becomes a bottleneck
const float StorageExpansionThreshold = 0.85f;
var storageExpansionCandidates = new[]
{
("solid", "bulk-bay"),
("liquid", "liquid-tank"),
("container", "container-bay"),
};
foreach (var (storageClass, moduleId) in storageExpansionCandidates)
{
var capacity = GetStationStorageCapacity(station, storageClass);
if (capacity <= 0.01f)
{
continue;
}
var used = station.Inventory
.Where(entry => world.ItemDefinitions.TryGetValue(entry.Key, out var def) && def.CargoKind == storageClass)
.Sum(entry => entry.Value);
if (used / capacity >= StorageExpansionThreshold && world.ModuleRecipes.ContainsKey(moduleId))
{
return moduleId;
}
}
var priorities = GetFactionExpansionPressure(world, station.FactionId) > 0f
? new (string ModuleId, int TargetCount)[]
{
("refinery-stack", 1),
("bulk-bay", 1),
("container-bay", 1),
("fabricator-array", 2),
("component-factory", 1),
@@ -120,6 +148,7 @@ public sealed partial class SimulationEngine
: new (string ModuleId, int TargetCount)[]
{
("refinery-stack", 1),
("bulk-bay", 1),
("container-bay", 1),
("fabricator-array", 2),
("component-factory", 1),

View File

@@ -25,17 +25,14 @@ public sealed partial class SimulationEngine
ship.TrackedActionTotal = MathF.Max(total, 0.01f);
}
internal static float GetShipCargoAmount(ShipRuntime ship)
{
var cargoItemId = ship.Definition.CargoItemId;
return cargoItemId is null ? 0f : GetInventoryAmount(ship.Inventory, cargoItemId);
}
internal static float GetShipCargoAmount(ShipRuntime ship) =>
ship.Inventory.Values.Sum();
private string UpdateExtract(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
{
var task = ship.ControllerTask;
var node = world.Nodes.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
if (node is null || task.TargetPosition is null || !CanExtractNode(ship, node))
if (node is null || task.TargetPosition is null || !CanExtractNode(ship, node, world))
{
ship.State = ShipState.Idle;
ship.TargetPosition = ship.Position;
@@ -79,10 +76,7 @@ public sealed partial class SimulationEngine
return node.OreRemaining <= 0.01f ? "node-depleted" : "cargo-full";
}
if (ship.Definition.CargoItemId is not null)
{
AddInventory(ship.Inventory, ship.Definition.CargoItemId, mined);
}
AddInventory(ship.Inventory, node.ItemId, mined);
node.OreRemaining -= mined;
node.OreRemaining = MathF.Max(0f, node.OreRemaining);
@@ -167,23 +161,21 @@ public sealed partial class SimulationEngine
ship.ActionTimer = 0f;
ship.State = ShipState.Transferring;
BeginTrackedAction(ship, "transferring", GetShipCargoAmount(ship));
var cargoItemId = ship.Definition.CargoItemId;
var moved = cargoItemId is null ? 0f : MathF.Min(GetInventoryAmount(ship.Inventory, cargoItemId), world.Balance.TransferRate * deltaSeconds);
if (cargoItemId is not null)
{
var accepted = TryAddStationInventory(world, station, cargoItemId, moved);
RemoveInventory(ship.Inventory, cargoItemId, accepted);
moved = accepted;
}
var faction = world.Factions.FirstOrDefault(candidate => candidate.Id == ship.FactionId);
if (faction is not null && cargoItemId == "ore")
foreach (var (itemId, amount) in ship.Inventory.ToList())
{
faction.OreMined += moved;
faction.Credits += moved * 0.4f;
var moved = MathF.Min(amount, world.Balance.TransferRate * deltaSeconds);
var accepted = TryAddStationInventory(world, station, itemId, moved);
RemoveInventory(ship.Inventory, itemId, accepted);
if (faction is not null && string.Equals(itemId, "ore", StringComparison.Ordinal))
{
faction.OreMined += accepted;
faction.Credits += accepted * 0.4f;
}
}
return cargoItemId is null || GetInventoryAmount(ship.Inventory, cargoItemId) <= 0.01f ? "unloaded" : "none";
return GetShipCargoAmount(ship) <= 0.01f ? "unloaded" : "none";
}
private string UpdateLoadCargo(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
@@ -209,19 +201,19 @@ public sealed partial class SimulationEngine
ship.Position = ship.TargetPosition;
ship.ActionTimer = 0f;
ship.State = ShipState.Loading;
var itemId = ship.ControllerTask.ItemId;
BeginTrackedAction(ship, "loading", MathF.Max(0f, ship.Definition.CargoCapacity - GetShipCargoAmount(ship)));
var cargoItemId = ship.Definition.CargoItemId;
var transfer = MathF.Min(world.Balance.TransferRate * deltaSeconds, ship.Definition.CargoCapacity - GetShipCargoAmount(ship));
var moved = cargoItemId is null ? 0f : MathF.Min(transfer, GetInventoryAmount(station.Inventory, cargoItemId));
if (cargoItemId is not null && moved > 0.01f)
var moved = itemId is null ? 0f : MathF.Min(transfer, GetInventoryAmount(station.Inventory, itemId));
if (itemId is not null && moved > 0.01f)
{
RemoveInventory(station.Inventory, cargoItemId, moved);
AddInventory(ship.Inventory, cargoItemId, moved);
RemoveInventory(station.Inventory, itemId, moved);
AddInventory(ship.Inventory, itemId, moved);
}
return cargoItemId is null
return itemId is null
|| GetShipCargoAmount(ship) >= ship.Definition.CargoCapacity - 0.01f
|| GetInventoryAmount(station.Inventory, cargoItemId) <= 0.01f
|| GetInventoryAmount(station.Inventory, itemId) <= 0.01f
? "loaded"
: "none";
}
@@ -411,65 +403,6 @@ public sealed partial class SimulationEngine
private static bool IsShipWithinSupportRange(ShipRuntime ship, Vector3 supportPosition, float threshold) =>
ship.Position.DistanceTo(supportPosition) <= MathF.Max(threshold, 6f);
private string UpdateLoadWorkers(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
{
if (ship.DockedStationId is null || !CanTransportWorkers(ship))
{
ship.State = ShipState.Blocked;
return "failed";
}
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
if (station is null || station.Population <= 0.01f)
{
ship.State = ShipState.Idle;
return "none";
}
var transfer = MathF.Min(station.Population, GetWorkerTransportCapacity(ship) - ship.WorkerPopulation);
var totalTransfer = MathF.Min(station.Population, GetWorkerTransportCapacity(ship) - ship.WorkerPopulation);
transfer = MathF.Min(transfer, 4f * deltaSeconds);
if (transfer <= 0.01f)
{
return "none";
}
station.Population = MathF.Max(0f, station.Population - transfer);
ship.WorkerPopulation += transfer;
ship.State = ShipState.Loading;
BeginTrackedAction(ship, "loading", totalTransfer);
return ship.WorkerPopulation >= GetWorkerTransportCapacity(ship) - 0.01f ? "workers-loaded" : "none";
}
private string UpdateUnloadWorkers(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
{
if (ship.DockedStationId is null || !CanTransportWorkers(ship))
{
ship.State = ShipState.Blocked;
return "failed";
}
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
if (station is null || ship.WorkerPopulation <= 0.01f)
{
ship.State = ShipState.Idle;
return "none";
}
var transfer = MathF.Min(ship.WorkerPopulation, MathF.Max(0f, station.PopulationCapacity - station.Population));
var totalTransfer = transfer;
transfer = MathF.Min(transfer, 4f * deltaSeconds);
if (transfer <= 0.01f)
{
return "none";
}
ship.WorkerPopulation = MathF.Max(0f, ship.WorkerPopulation - transfer);
station.Population = MathF.Min(station.PopulationCapacity, station.Population + transfer);
ship.State = ShipState.Unloading;
BeginTrackedAction(ship, "unloading", totalTransfer);
return ship.WorkerPopulation <= 0.01f ? "workers-unloaded" : "none";
}
private string UpdateUndock(ShipRuntime ship, SimulationWorld world, float deltaSeconds)
{

View File

@@ -152,7 +152,7 @@ public sealed partial class SimulationEngine
.FirstOrDefault()
: world.Nodes.FirstOrDefault(candidate => candidate.Id == behavior.NodeId && candidate.OreRemaining > 0.01f);
if (refinery is null || node is null || !HasShipModules(ship.Definition, "reactor-core", "capacitor-bank", requiredModule))
if (refinery is null || node is null || !HasShipCapabilities(ship.Definition, requiredModule))
{
behavior.Kind = "idle";
ship.ControllerTask = CreateIdleTask(world.Balance.ArrivalThreshold);
@@ -505,8 +505,7 @@ public sealed partial class SimulationEngine
"unload" => ControllerTaskKind.Unload,
"deliver-construction" => ControllerTaskKind.DeliverConstruction,
"build-construction-site" => ControllerTaskKind.BuildConstructionSite,
"load-workers" => ControllerTaskKind.LoadWorkers,
"unload-workers" => ControllerTaskKind.UnloadWorkers,
"construct-module" => ControllerTaskKind.ConstructModule,
"undock" => ControllerTaskKind.Undock,
_ => ControllerTaskKind.Idle,

View File

@@ -235,7 +235,7 @@ public sealed partial class SimulationEngine
return false;
}
if (!string.Equals(shipDefinition.Role, "military", StringComparison.Ordinal)
if (!string.Equals(shipDefinition.Kind, "military", StringComparison.Ordinal)
|| !FactionNeedsMoreWarships(world, station.FactionId))
{
return false;
@@ -394,7 +394,7 @@ public sealed partial class SimulationEngine
{
var militaryShipCount = world.Ships.Count(ship =>
ship.FactionId == factionId
&& string.Equals(ship.Definition.Role, "military", StringComparison.Ordinal));
&& string.Equals(ship.Definition.Kind, "military", StringComparison.Ordinal));
var targetSystems = Math.Max(1, Math.Min(StrategicControlTargetSystems, world.Systems.Count));
var controlledSystems = GetFactionControlledSystemsCount(world, factionId);
var expansionDeficit = Math.Max(0, targetSystems - controlledSystems);
@@ -448,7 +448,7 @@ public sealed partial class SimulationEngine
private static DefaultBehaviorRuntime CreateSpawnedShipBehavior(ShipDefinition definition, StationRuntime station)
{
if (!string.Equals(definition.Role, "military", StringComparison.Ordinal))
if (!string.Equals(definition.Kind, "military", StringComparison.Ordinal))
{
return new DefaultBehaviorRuntime
{