feat: massive AI generation
This commit is contained in:
@@ -15,10 +15,16 @@ internal sealed class WorldBuilder(
|
||||
var systems = generationService.ExpandSystems(
|
||||
generationService.InjectSpecialSystems(catalog.AuthoredSystems),
|
||||
worldGeneration.TargetSystemCount);
|
||||
|
||||
Console.WriteLine("TEST");
|
||||
Console.WriteLine(string.Join(',', systems.Select(s => s.Id)));
|
||||
|
||||
var scenario = dataLoader.NormalizeScenarioToAvailableSystems(
|
||||
catalog.Scenario,
|
||||
systems.Select(system => system.Id).ToList());
|
||||
|
||||
Console.WriteLine(string.Join(',', systems.Select(s => s.Id)));
|
||||
|
||||
var systemRuntimes = systems
|
||||
.Select(definition => new SystemRuntime
|
||||
{
|
||||
@@ -42,11 +48,29 @@ internal sealed class WorldBuilder(
|
||||
var patrolRoutes = BuildPatrolRoutes(scenario, systemsById);
|
||||
var ships = CreateShips(scenario, systemsById, spatialLayout.Celestials, catalog.Balance, catalog.ShipDefinitions, patrolRoutes, stations, refinery);
|
||||
|
||||
if (worldGeneration.AiControllerFactionCount < int.MaxValue)
|
||||
{
|
||||
var aiFactionIds = stations
|
||||
.Select(s => s.FactionId)
|
||||
.Concat(ships.Select(s => s.FactionId))
|
||||
.Where(id => !string.IsNullOrWhiteSpace(id) && !string.Equals(id, DefaultFactionId, StringComparison.Ordinal))
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.OrderBy(id => id, StringComparer.Ordinal)
|
||||
.Take(worldGeneration.AiControllerFactionCount)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
aiFactionIds.Add(DefaultFactionId);
|
||||
stations = stations.Where(s => aiFactionIds.Contains(s.FactionId)).ToList();
|
||||
ships = ships.Where(s => aiFactionIds.Contains(s.FactionId)).ToList();
|
||||
}
|
||||
|
||||
var factions = seedingService.CreateFactions(stations, ships);
|
||||
seedingService.BootstrapFactionEconomy(factions, stations);
|
||||
var policies = seedingService.CreatePolicies(factions);
|
||||
var commanders = seedingService.CreateCommanders(factions, stations, ships);
|
||||
var nowUtc = DateTimeOffset.UtcNow;
|
||||
var playerFaction = worldGeneration.GeneratePlayerFaction
|
||||
? seedingService.CreatePlayerFaction(factions, stations, ships, commanders, policies, nowUtc)
|
||||
: null;
|
||||
var claims = seedingService.CreateClaims(stations, spatialLayout.Celestials, nowUtc);
|
||||
var bootstrapWorld = new SimulationWorld
|
||||
{
|
||||
@@ -56,9 +80,11 @@ internal sealed class WorldBuilder(
|
||||
Systems = systemRuntimes,
|
||||
Celestials = spatialLayout.Celestials,
|
||||
Nodes = spatialLayout.Nodes,
|
||||
Wrecks = [],
|
||||
Stations = stations,
|
||||
Ships = ships,
|
||||
Factions = factions,
|
||||
PlayerFaction = playerFaction,
|
||||
Commanders = commanders,
|
||||
Claims = claims,
|
||||
ConstructionSites = [],
|
||||
@@ -75,7 +101,7 @@ internal sealed class WorldBuilder(
|
||||
};
|
||||
var (constructionSites, marketOrders) = seedingService.CreateConstructionSites(bootstrapWorld);
|
||||
|
||||
return new SimulationWorld
|
||||
var world = new SimulationWorld
|
||||
{
|
||||
Label = "Split Viewer / Simulation World",
|
||||
Seed = WorldSeed,
|
||||
@@ -83,9 +109,12 @@ internal sealed class WorldBuilder(
|
||||
Systems = systemRuntimes,
|
||||
Celestials = spatialLayout.Celestials,
|
||||
Nodes = spatialLayout.Nodes,
|
||||
Wrecks = [],
|
||||
Stations = stations,
|
||||
Ships = ships,
|
||||
Factions = factions,
|
||||
PlayerFaction = playerFaction,
|
||||
Geopolitics = null,
|
||||
Commanders = commanders,
|
||||
Claims = claims,
|
||||
ConstructionSites = constructionSites,
|
||||
@@ -100,6 +129,10 @@ internal sealed class WorldBuilder(
|
||||
OrbitalTimeSeconds = WorldSeed * 97d,
|
||||
GeneratedAtUtc = DateTimeOffset.UtcNow,
|
||||
};
|
||||
|
||||
var geopolitics = new GeopoliticalSimulationService();
|
||||
geopolitics.Update(world, 0f, []);
|
||||
return world;
|
||||
}
|
||||
|
||||
private static List<StationRuntime> CreateStations(
|
||||
@@ -291,7 +324,7 @@ internal sealed class WorldBuilder(
|
||||
patrolRoutes,
|
||||
stations,
|
||||
refinery),
|
||||
ControllerTask = new ControllerTaskRuntime { Kind = ControllerTaskKind.Idle, Threshold = balance.ArrivalThreshold, Status = WorkStatus.Pending },
|
||||
Skills = WorldSeedingService.CreateSkills(definition),
|
||||
Health = definition.MaxHealth,
|
||||
});
|
||||
|
||||
|
||||
@@ -286,16 +286,9 @@ internal sealed class WorldSeedingService
|
||||
FactionId = faction.Id,
|
||||
ControlledEntityId = faction.Id,
|
||||
PolicySetId = faction.DefaultPolicySetId,
|
||||
Doctrine = "strategic-expansionist",
|
||||
Doctrine = "strategic-control",
|
||||
};
|
||||
|
||||
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);
|
||||
@@ -316,7 +309,7 @@ internal sealed class WorldSeedingService
|
||||
ParentCommanderId = parentCommander.Id,
|
||||
ControlledEntityId = station.Id,
|
||||
PolicySetId = parentCommander.PolicySetId,
|
||||
Doctrine = "station-default",
|
||||
Doctrine = "station-control",
|
||||
};
|
||||
|
||||
station.CommanderId = commander.Id;
|
||||
@@ -341,16 +334,9 @@ internal sealed class WorldSeedingService
|
||||
ParentCommanderId = parentCommander.Id,
|
||||
ControlledEntityId = ship.Id,
|
||||
PolicySetId = parentCommander.PolicySetId,
|
||||
Doctrine = "ship-default",
|
||||
ActiveBehavior = CopyBehavior(ship.DefaultBehavior),
|
||||
ActiveTask = CopyTask(ship.ControllerTask, null),
|
||||
Doctrine = "ship-control",
|
||||
};
|
||||
|
||||
if (ship.Order is not null)
|
||||
{
|
||||
commander.ActiveOrder = CopyOrder(ship.Order);
|
||||
}
|
||||
|
||||
ship.CommanderId = commander.Id;
|
||||
ship.PolicySetId = parentCommander.PolicySetId;
|
||||
parentCommander.SubordinateCommanderIds.Add(commander.Id);
|
||||
@@ -361,6 +347,93 @@ internal sealed class WorldSeedingService
|
||||
return commanders;
|
||||
}
|
||||
|
||||
internal PlayerFactionRuntime CreatePlayerFaction(
|
||||
IReadOnlyCollection<FactionRuntime> factions,
|
||||
IReadOnlyCollection<StationRuntime> stations,
|
||||
IReadOnlyCollection<ShipRuntime> ships,
|
||||
IReadOnlyCollection<CommanderRuntime> commanders,
|
||||
IReadOnlyCollection<PolicySetRuntime> policies,
|
||||
DateTimeOffset nowUtc)
|
||||
{
|
||||
var sovereignFaction = factions.FirstOrDefault(faction => string.Equals(faction.Id, DefaultFactionId, StringComparison.Ordinal))
|
||||
?? factions.OrderBy(faction => faction.Id, StringComparer.Ordinal).First();
|
||||
|
||||
var player = new PlayerFactionRuntime
|
||||
{
|
||||
Id = "player-faction",
|
||||
Label = $"{sovereignFaction.Label} Command",
|
||||
SovereignFactionId = sovereignFaction.Id,
|
||||
CreatedAtUtc = nowUtc,
|
||||
UpdatedAtUtc = nowUtc,
|
||||
};
|
||||
|
||||
foreach (var shipId in ships.Where(ship => ship.FactionId == sovereignFaction.Id).Select(ship => ship.Id))
|
||||
{
|
||||
player.AssetRegistry.ShipIds.Add(shipId);
|
||||
}
|
||||
|
||||
foreach (var stationId in stations.Where(station => station.FactionId == sovereignFaction.Id).Select(station => station.Id))
|
||||
{
|
||||
player.AssetRegistry.StationIds.Add(stationId);
|
||||
}
|
||||
|
||||
foreach (var commanderId in commanders.Where(commander => commander.FactionId == sovereignFaction.Id).Select(commander => commander.Id))
|
||||
{
|
||||
player.AssetRegistry.CommanderIds.Add(commanderId);
|
||||
}
|
||||
|
||||
foreach (var policy in policies.Where(policy => string.Equals(policy.OwnerId, sovereignFaction.Id, StringComparison.Ordinal)))
|
||||
{
|
||||
player.AssetRegistry.PolicySetIds.Add(policy.Id);
|
||||
}
|
||||
|
||||
player.Policies.Add(new PlayerFactionPolicyRuntime
|
||||
{
|
||||
Id = "player-core-policy",
|
||||
Label = "Core Empire Policy",
|
||||
ScopeKind = "player-faction",
|
||||
ScopeId = player.Id,
|
||||
PolicySetId = sovereignFaction.DefaultPolicySetId,
|
||||
TradeAccessPolicy = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.TradeAccessPolicy ?? "owner-and-allies",
|
||||
DockingAccessPolicy = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.DockingAccessPolicy ?? "owner-and-allies",
|
||||
ConstructionAccessPolicy = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.ConstructionAccessPolicy ?? "owner-only",
|
||||
OperationalRangePolicy = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.OperationalRangePolicy ?? "unrestricted",
|
||||
CombatEngagementPolicy = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.CombatEngagementPolicy ?? "defensive",
|
||||
AvoidHostileSystems = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.AvoidHostileSystems ?? true,
|
||||
FleeHullRatio = policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId)?.FleeHullRatio ?? 0.35f,
|
||||
UpdatedAtUtc = nowUtc,
|
||||
});
|
||||
|
||||
if (policies.FirstOrDefault(policy => policy.Id == sovereignFaction.DefaultPolicySetId) is { } defaultPolicy)
|
||||
{
|
||||
foreach (var systemId in defaultPolicy.BlacklistedSystemIds)
|
||||
{
|
||||
player.Policies[0].BlacklistedSystemIds.Add(systemId);
|
||||
}
|
||||
}
|
||||
|
||||
player.AutomationPolicies.Add(new PlayerAutomationPolicyRuntime
|
||||
{
|
||||
Id = "player-core-automation",
|
||||
Label = "Core Automation",
|
||||
ScopeKind = "player-faction",
|
||||
ScopeId = player.Id,
|
||||
BehaviorKind = "idle",
|
||||
UpdatedAtUtc = nowUtc,
|
||||
});
|
||||
|
||||
player.Reserves.Add(new PlayerReserveGroupRuntime
|
||||
{
|
||||
Id = "player-core-reserve",
|
||||
Label = "Strategic Reserve",
|
||||
ReserveKind = "military",
|
||||
UpdatedAtUtc = nowUtc,
|
||||
});
|
||||
player.AssetRegistry.ReserveIds.Add("player-core-reserve");
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
internal static DefaultBehaviorRuntime CreateBehavior(
|
||||
ShipDefinition definition,
|
||||
string systemId,
|
||||
@@ -381,22 +454,32 @@ internal sealed class WorldSeedingService
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "construct-station",
|
||||
StationId = homeStation.Id,
|
||||
Phase = "travel-to-station",
|
||||
HomeSystemId = homeStation.SystemId,
|
||||
HomeStationId = homeStation.Id,
|
||||
PreferredConstructionSiteId = null,
|
||||
};
|
||||
}
|
||||
|
||||
if (HasCapabilities(definition, "mining") && homeStation is not null)
|
||||
{
|
||||
return CreateResourceHarvestBehavior("auto-mine", scenario.MiningDefaults.NodeSystemId, homeStation.Id);
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = definition.CargoCapacity >= 120f ? "expert-auto-mine" : "advanced-auto-mine",
|
||||
HomeSystemId = homeStation.SystemId,
|
||||
HomeStationId = homeStation.Id,
|
||||
AreaSystemId = scenario.MiningDefaults.NodeSystemId,
|
||||
MaxSystemRange = definition.CargoCapacity >= 120f ? 3 : 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (string.Equals(definition.Kind, "transport", StringComparison.Ordinal))
|
||||
{
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "trade-haul",
|
||||
Phase = "travel-to-source",
|
||||
Kind = "advanced-auto-trade",
|
||||
HomeSystemId = homeStation?.SystemId ?? systemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
MaxSystemRange = 2,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -405,7 +488,9 @@ internal sealed class WorldSeedingService
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "patrol",
|
||||
StationId = homeStation?.Id,
|
||||
HomeSystemId = homeStation?.SystemId ?? systemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
AreaSystemId = systemId,
|
||||
PatrolPoints = route,
|
||||
PatrolIndex = 0,
|
||||
};
|
||||
@@ -414,6 +499,20 @@ internal sealed class WorldSeedingService
|
||||
return new DefaultBehaviorRuntime
|
||||
{
|
||||
Kind = "idle",
|
||||
HomeSystemId = homeStation?.SystemId ?? systemId,
|
||||
HomeStationId = homeStation?.Id,
|
||||
};
|
||||
}
|
||||
|
||||
internal static ShipSkillProfileRuntime CreateSkills(ShipDefinition definition)
|
||||
{
|
||||
return definition.Kind switch
|
||||
{
|
||||
"transport" => new ShipSkillProfileRuntime { Navigation = 3, Trade = 4, Mining = 1, Combat = 1, Construction = 1 },
|
||||
"construction" => new ShipSkillProfileRuntime { Navigation = 3, Trade = 1, Mining = 1, Combat = 1, Construction = 4 },
|
||||
"military" => new ShipSkillProfileRuntime { Navigation = 4, Trade = 1, Mining = 1, Combat = 4, Construction = 1 },
|
||||
_ when HasCapabilities(definition, "mining") => new ShipSkillProfileRuntime { Navigation = 3, Trade = 1, Mining = 4, Combat = 1, Construction = 1 },
|
||||
_ => new ShipSkillProfileRuntime { Navigation = 3, Trade = 2, Mining = 1, Combat = 1, Construction = 1 },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -471,43 +570,4 @@ internal sealed class WorldSeedingService
|
||||
.Select(segment => char.ToUpperInvariant(segment[0]) + segment[1..]));
|
||||
}
|
||||
|
||||
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,
|
||||
AreaSystemId = behavior.AreaSystemId,
|
||||
TargetEntityId = behavior.TargetEntityId,
|
||||
ItemId = behavior.ItemId,
|
||||
ModuleId = behavior.ModuleId,
|
||||
NodeId = behavior.NodeId,
|
||||
Phase = behavior.Phase,
|
||||
PatrolIndex = behavior.PatrolIndex,
|
||||
StationId = behavior.StationId,
|
||||
};
|
||||
|
||||
private static CommanderOrderRuntime CopyOrder(ShipOrderRuntime order) => new()
|
||||
{
|
||||
Kind = order.Kind,
|
||||
Status = order.Status,
|
||||
DestinationSystemId = order.DestinationSystemId,
|
||||
DestinationPosition = order.DestinationPosition,
|
||||
};
|
||||
|
||||
private static CommanderTaskRuntime CopyTask(ControllerTaskRuntime task, string? targetNodeId) => new()
|
||||
{
|
||||
Kind = task.Kind.ToContractValue(),
|
||||
Status = task.Status,
|
||||
TargetEntityId = task.TargetEntityId,
|
||||
TargetNodeId = targetNodeId ?? task.TargetNodeId,
|
||||
TargetPosition = task.TargetPosition,
|
||||
TargetSystemId = task.TargetSystemId,
|
||||
Threshold = task.Threshold,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user