feat: simplifying the simulation
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user