feat: production chain

This commit is contained in:
2026-03-15 22:46:47 -04:00
parent 651556c916
commit 5ba1287f85
65 changed files with 3718 additions and 687 deletions

View File

@@ -21,7 +21,12 @@ public sealed partial class ScenarioLoader
factionIds.Add(DefaultFactionId);
}
return factionIds.Select(CreateFaction).ToList();
factionIds.Add(UnclaimedFactionId);
return factionIds
.Distinct(StringComparer.Ordinal)
.Select(CreateFaction)
.ToList();
}
private static FactionRuntime CreateFaction(string factionId)
@@ -35,6 +40,13 @@ public sealed partial class ScenarioLoader
Color = "#7ed4ff",
Credits = MinimumFactionCredits,
},
UnclaimedFactionId => new FactionRuntime
{
Id = factionId,
Label = "Unclaimed",
Color = "#7f8794",
Credits = 0f,
},
_ => new FactionRuntime
{
Id = factionId,
@@ -89,30 +101,32 @@ public sealed partial class ScenarioLoader
IReadOnlyCollection<NodeRuntime> nodes,
DateTimeOffset nowUtc)
{
var claims = new List<ClaimRuntime>(stations.Count);
foreach (var station in stations)
var stationsByAnchorNodeId = stations
.Where((station) => station.AnchorNodeId is not null)
.ToDictionary((station) => station.AnchorNodeId!, StringComparer.Ordinal);
var claims = new List<ClaimRuntime>();
foreach (var node in nodes.Where((candidate) => candidate.Kind == SpatialNodeKind.LagrangePoint))
{
if (station.AnchorNodeId is null)
{
continue;
}
var anchorNode = nodes.FirstOrDefault((node) => node.Id == station.AnchorNodeId);
if (anchorNode is null)
{
continue;
}
var owningFactionId = stationsByAnchorNodeId.TryGetValue(node.Id, out var station)
? station.FactionId
: UnclaimedFactionId;
var activatesAtUtc = owningFactionId == UnclaimedFactionId
? nowUtc
: nowUtc.AddSeconds(8);
var state = owningFactionId == UnclaimedFactionId
? ClaimStateKinds.Active
: ClaimStateKinds.Activating;
claims.Add(new ClaimRuntime
{
Id = $"claim-{station.Id}",
FactionId = station.FactionId,
SystemId = station.SystemId,
NodeId = anchorNode.Id,
BubbleId = anchorNode.BubbleId,
Id = $"claim-{node.Id}",
FactionId = owningFactionId,
SystemId = node.SystemId,
NodeId = node.Id,
BubbleId = node.BubbleId,
PlacedAtUtc = nowUtc,
ActivatesAtUtc = nowUtc.AddSeconds(8),
State = ClaimStateKinds.Activating,
ActivatesAtUtc = activatesAtUtc,
State = state,
Health = 100f,
});
}
@@ -138,8 +152,13 @@ public sealed partial class ScenarioLoader
}
var anchorNode = nodes.FirstOrDefault((node) => node.Id == station.AnchorNodeId);
var claim = claims.FirstOrDefault((candidate) => candidate.Id == $"claim-{station.Id}");
if (anchorNode is null || claim is null || !moduleRecipes.TryGetValue(moduleId, out var recipe))
if (anchorNode is null)
{
continue;
}
var claim = claims.FirstOrDefault((candidate) => candidate.NodeId == anchorNode.Id);
if (claim is null || !moduleRecipes.TryGetValue(moduleId, out var recipe))
{
continue;
}
@@ -192,9 +211,20 @@ public sealed partial class ScenarioLoader
StationRuntime station,
IReadOnlyDictionary<string, ModuleRecipeDefinition> moduleRecipes)
{
foreach (var moduleId in new[] { "gas-tank", "fuel-processor", "refinery-stack", "dock-bay-small" })
foreach (var (moduleId, targetCount) in new (string ModuleId, int TargetCount)[]
{
if (!station.InstalledModules.Contains(moduleId, StringComparer.Ordinal)
("gas-tank", 1),
("fuel-processor", 1),
("refinery-stack", 1),
("container-bay", 1),
("fabricator-array", 2),
("component-factory", 1),
("ship-factory", 1),
("solar-array", 2),
("dock-bay-small", 2),
})
{
if (CountModules(station.InstalledModules, moduleId) < targetCount
&& moduleRecipes.ContainsKey(moduleId))
{
return moduleId;
@@ -220,6 +250,11 @@ public sealed partial class ScenarioLoader
var policies = new List<PolicySetRuntime>(factions.Count);
foreach (var faction in factions)
{
if (string.Equals(faction.Id, UnclaimedFactionId, StringComparison.Ordinal))
{
continue;
}
var policyId = $"policy-{faction.Id}";
faction.DefaultPolicySetId = policyId;
policies.Add(new PolicySetRuntime
@@ -244,6 +279,11 @@ public sealed partial class ScenarioLoader
foreach (var faction in factions)
{
if (string.Equals(faction.Id, UnclaimedFactionId, StringComparison.Ordinal))
{
continue;
}
var commander = new CommanderRuntime
{
Id = $"commander-faction-{faction.Id}",
@@ -251,9 +291,16 @@ public sealed partial class ScenarioLoader
FactionId = faction.Id,
ControlledEntityId = faction.Id,
PolicySetId = faction.DefaultPolicySetId,
Doctrine = "strategic-default",
Doctrine = "strategic-expansionist",
};
commander.Goals.Add("control-all-systems");
commander.Goals.Add("control-five-systems-fast");
commander.Goals.Add("expand-industrial-base");
commander.Goals.Add("grow-war-fleet");
commander.Goals.Add("deter-pirate-harassment");
commander.Goals.Add("contest-rival-expansion");
commanders.Add(commander);
factionCommanders[faction.Id] = commander;
faction.CommanderIds.Add(commander.Id);
@@ -334,7 +381,7 @@ public sealed partial class ScenarioLoader
IReadOnlyDictionary<string, List<Vector3>> patrolRoutes,
StationRuntime? refinery)
{
if (HasModules(definition, "fabricator-array", "docking-clamps") && refinery is not null)
if (string.Equals(definition.Role, "construction", StringComparison.Ordinal) && refinery is not null)
{
return new DefaultBehaviorRuntime
{
@@ -345,24 +392,23 @@ public sealed partial class ScenarioLoader
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "gas-extractor") && refinery is not null)
{
return CreateResourceHarvestBehavior("auto-harvest-gas", scenario.MiningDefaults.NodeSystemId, refinery.Id);
}
if (string.Equals(definition.Role, "transport", StringComparison.Ordinal) && refinery is not null)
{
return new DefaultBehaviorRuntime
{
Kind = "auto-harvest-gas",
Kind = "auto-supply-energy",
StationId = refinery.Id,
Phase = "travel-to-node",
Phase = "travel-to-source",
};
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "mining-turret") && refinery is not null)
{
return new DefaultBehaviorRuntime
{
Kind = "auto-mine",
AreaSystemId = scenario.MiningDefaults.NodeSystemId,
StationId = refinery.Id,
Phase = "travel-to-node",
};
return CreateResourceHarvestBehavior("auto-mine", scenario.MiningDefaults.NodeSystemId, refinery.Id);
}
if (HasModules(definition, "reactor-core", "capacitor-bank", "gun-turret") && patrolRoutes.TryGetValue(systemId, out var route))
@@ -381,6 +427,14 @@ public sealed partial class ScenarioLoader
};
}
private static DefaultBehaviorRuntime CreateResourceHarvestBehavior(string kind, string areaSystemId, string stationId) => new()
{
Kind = kind,
AreaSystemId = areaSystemId,
StationId = stationId,
Phase = "travel-to-node",
};
private static CommanderBehaviorRuntime CopyBehavior(DefaultBehaviorRuntime behavior) => new()
{
Kind = behavior.Kind,
@@ -402,7 +456,7 @@ public sealed partial class ScenarioLoader
private static CommanderTaskRuntime CopyTask(ControllerTaskRuntime task, string? targetNodeId) => new()
{
Kind = task.Kind,
Kind = task.Kind.ToContractValue(),
Status = task.Status,
TargetEntityId = task.TargetEntityId,
TargetNodeId = targetNodeId ?? task.TargetNodeId,