Refactor runtime bootstrap and ship control flows

This commit is contained in:
2026-04-03 01:12:26 -04:00
parent 0bb72bee35
commit 706e1cda8f
129 changed files with 9588 additions and 3548 deletions

View File

@@ -1,5 +1,6 @@
using SpaceGame.Api.Shared.Runtime;
using SpaceGame.Api.Ships.Simulation;
using SpaceGame.Api.Ships.AI;
using static SpaceGame.Api.Shared.Runtime.ShipBehaviorKinds;
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;
namespace SpaceGame.Api.Stations.Simulation;
@@ -81,7 +82,7 @@ internal sealed class StationLifecycleService
SpatialState = CreateSpawnedShipSpatialState(station, spawnPosition),
DefaultBehavior = CreateSpawnedShipBehavior(definition, station),
Skills = ShipBootstrapPolicy.CreateSkills(definition),
Health = definition.MaxHealth,
Health = definition.Hull,
};
world.Ships.Add(ship);
@@ -91,7 +92,7 @@ internal sealed class StationLifecycleService
faction.ShipsBuilt += 1;
}
events.Add(new SimulationEventRecord("station", station.Id, "ship-built", $"{station.Label} launched {definition.Label}.", DateTimeOffset.UtcNow));
events.Add(new SimulationEventRecord("station", station.Id, "ship-built", $"{station.Label} launched {definition.Name}.", DateTimeOffset.UtcNow));
return 1f;
}
@@ -107,21 +108,22 @@ internal sealed class StationLifecycleService
private static DefaultBehaviorRuntime CreateSpawnedShipBehavior(ShipDefinition definition, StationRuntime station)
{
if (!string.Equals(definition.Kind, "military", StringComparison.Ordinal))
if (!IsMilitaryShip(definition))
{
return new DefaultBehaviorRuntime
{
Kind = string.Equals(definition.Kind, "transport", StringComparison.Ordinal) ? "advanced-auto-trade" : "idle",
Kind = IsTransportShip(definition) ? AdvancedAutoTrade : HoldPosition,
HomeSystemId = station.SystemId,
HomeStationId = station.Id,
MaxSystemRange = string.Equals(definition.Kind, "transport", StringComparison.Ordinal) ? 2 : 0,
AreaSystemId = station.SystemId,
MaxSystemRange = IsTransportShip(definition) ? 2 : 0,
};
}
var patrolRadius = station.Radius + 90f;
return new DefaultBehaviorRuntime
{
Kind = "patrol",
Kind = Patrol,
HomeSystemId = station.SystemId,
HomeStationId = station.Id,
AreaSystemId = station.SystemId,

View File

@@ -1,4 +1,5 @@
using static SpaceGame.Api.Factions.AI.CommanderPlanningService;
using static SpaceGame.Api.Shared.Runtime.KnownShipTypes;
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;
using SpaceGame.Api.Shared.Runtime;
@@ -7,6 +8,10 @@ namespace SpaceGame.Api.Stations.Simulation;
internal sealed class StationSimulationService
{
internal const int StrategicControlTargetSystems = 5;
private const string MilitaryShipCategory = "military";
private const string ConstructionShipCategory = "construction";
private const string TransportShipCategory = "transport";
private const string MiningShipCategory = "mining";
internal void ReviewStationMarketOrders(SimulationWorld world, StationRuntime station)
{
@@ -63,7 +68,7 @@ internal sealed class StationSimulationService
var superfluidCoolantReserve = role == "superfluidcoolant" ? 120f : 0f;
var quantumTubesReserve = role == "quantumtubes" ? 120f : 0f;
var shipPartsReserve = HasShipyardCapability(station)
&& GetShipProductionPressure(world, station.FactionId, "military") > 0.2f
&& GetShipProductionPressure(world, station.FactionId, MilitaryShipCategory) > 0.2f
? 90f
: 0f;
@@ -118,7 +123,7 @@ internal sealed class StationSimulationService
var constructionClayReserve = GetConstructionDemandForItem(world, site, "claytronics");
var constructionRefinedReserve = GetConstructionDemandForItem(world, site, "refinedmetals");
var shipPartsReserve = HasShipyardCapability(station)
&& GetShipProductionPressure(world, station.FactionId, "military") > 0.2f
&& GetShipProductionPressure(world, station.FactionId, MilitaryShipCategory) > 0.2f
? 90f
: 0f;
@@ -255,7 +260,7 @@ internal sealed class StationSimulationService
var priority = (float)recipe.Priority;
var expansionPressure = GetFactionExpansionPressure(world, station.FactionId);
var fleetPressure = GetShipProductionPressure(world, station.FactionId, "military");
var fleetPressure = GetShipProductionPressure(world, station.FactionId, MilitaryShipCategory);
priority += GetStationRecipePriorityAdjustment(world, station, recipe, expansionPressure, fleetPressure);
priority += GetStrategicRecipeBias(world, station, recipe);
@@ -266,21 +271,34 @@ internal sealed class StationSimulationService
{
if (recipe.ShipOutputId is not null && world.ShipDefinitions.TryGetValue(recipe.ShipOutputId, out var shipDefinition))
{
var shipPressure = GetShipProductionPressure(world, station.FactionId, shipDefinition.Kind);
return shipDefinition.Kind switch
var shipPressure = GetShipProductionPressure(world, station.FactionId, GetShipCategory(shipDefinition));
if (IsMilitaryShip(shipDefinition))
{
"military" => recipe.Id switch
return recipe.Id switch
{
"frigate-construction" => 320f * shipPressure,
"destroyer-construction" => 200f * shipPressure,
"cruiser-construction" => 120f * shipPressure,
_ => 160f * shipPressure,
},
"construction" => 260f * shipPressure,
"mining" => 250f * shipPressure,
"transport" => 230f * shipPressure,
_ => 0f,
};
};
}
if (IsConstructionShip(shipDefinition))
{
return 260f * shipPressure;
}
if (IsMiningShip(shipDefinition))
{
return 250f * shipPressure;
}
if (IsTransportShip(shipDefinition))
{
return 230f * shipPressure;
}
return 0f;
}
var outputItemIds = recipe.Outputs
@@ -338,7 +356,7 @@ internal sealed class StationSimulationService
if (string.Equals(assignment.Kind, "ship-production-focus", StringComparison.Ordinal)
&& recipe.ShipOutputId is not null
&& world.ShipDefinitions.TryGetValue(recipe.ShipOutputId, out var shipDefinition)
&& string.Equals(shipDefinition.Kind, "military", StringComparison.Ordinal))
&& IsMilitaryShip(shipDefinition))
{
return 260f;
}
@@ -383,7 +401,7 @@ internal sealed class StationSimulationService
return false;
}
if (GetShipProductionPressure(world, station.FactionId, shipDefinition.Kind) <= 0.05f)
if (GetShipProductionPressure(world, station.FactionId, GetShipCategory(shipDefinition)) <= 0.05f)
{
return false;
}
@@ -708,7 +726,7 @@ internal sealed class StationSimulationService
.ToList();
}
private static float GetShipProductionPressure(SimulationWorld world, string factionId, string shipKind)
private static float GetShipProductionPressure(SimulationWorld world, string factionId, string? shipCategory)
{
var economic = FindFactionEconomicAssessment(world, factionId);
var threat = FindFactionThreatAssessment(world, factionId);
@@ -717,16 +735,16 @@ internal sealed class StationSimulationService
return 0f;
}
return shipKind switch
return shipCategory switch
{
"military" => threat.EnemyFactionCount > 0
MilitaryShipCategory => threat.EnemyFactionCount > 0
? economic.MilitaryShipCount < Math.Max(4, economic.ControlledSystemCount * 2) ? 1f : 0.25f
: 0.1f,
"construction" => economic.PrimaryExpansionSiteId is not null
ConstructionShipCategory => economic.PrimaryExpansionSiteId is not null
? economic.ConstructorShipCount < 1 ? 1f : 0.35f
: economic.ConstructorShipCount < 1 ? 0.5f : 0f,
"transport" => economic.TransportShipCount < Math.Max(2, economic.ControlledSystemCount) ? 0.8f : 0.2f,
_ when shipKind == "mining" || shipKind == "miner" => economic.MinerShipCount < Math.Max(2, economic.ControlledSystemCount) ? 0.85f : 0.2f,
TransportShipCategory => economic.TransportShipCount < Math.Max(2, economic.ControlledSystemCount) ? 0.8f : 0.2f,
MiningShipCategory => economic.MinerShipCount < Math.Max(2, economic.ControlledSystemCount) ? 0.85f : 0.2f,
_ => 0.15f,
};
}