using SpaceGame.Simulation.Api.Contracts; namespace SpaceGame.Simulation.Api.Simulation; public sealed partial class SimulationEngine { private const float ShipFuelToEnergyRatio = 12f; private const float StationFuelToEnergyRatio = 18f; private const float CapacitorEnergyPerModule = 120f; private const float StationEnergyPerPowerCore = 480f; private const float ShipFuelPerReactor = 100f; private const float StationFuelPerTank = 500f; private const float WaterConsumptionPerWorkerPerSecond = 0.004f; private const float PopulationGrowthPerSecond = 0.012f; private const float PopulationAttritionPerSecond = 0.018f; private static readonly ShipBehaviorStateMachine _shipBehaviorStateMachine = ShipBehaviorStateMachine.CreateDefault(); private static readonly IReadOnlyList _worldUpdatePipeline = [ new((engine, world, deltaSeconds, nowUtc, events) => UpdateOrbitalState(world, nowUtc)), new((engine, world, deltaSeconds, nowUtc, events) => UpdateClaims(world, events)), new((engine, world, deltaSeconds, nowUtc, events) => UpdateConstructionSites(world, events)), new((engine, world, deltaSeconds, nowUtc, events) => UpdateStationPower(world, deltaSeconds, events)), new((engine, world, deltaSeconds, nowUtc, events) => engine.UpdateStations(world, deltaSeconds, events)), ]; private static readonly IReadOnlyList _shipUpdatePipeline = [ new((engine, ship, world, deltaSeconds, events) => UpdateShipPower(ship, world, deltaSeconds, events)), new((engine, ship, world, deltaSeconds, events) => engine.RefreshControlLayers(ship, world)), new((engine, ship, world, deltaSeconds, events) => engine.PlanControllerTask(ship, world)), ]; public WorldDelta Tick(SimulationWorld world, float deltaSeconds, long sequence) { var events = new List(); var nowUtc = DateTimeOffset.UtcNow; foreach (var step in _worldUpdatePipeline) { step.Execute(this, world, deltaSeconds, nowUtc, events); } foreach (var ship in world.Ships) { var previousPosition = ship.Position; var previousState = ship.State; var previousBehavior = ship.DefaultBehavior.Kind; var previousTask = ship.ControllerTask.Kind; foreach (var step in _shipUpdatePipeline) { step.Execute(this, ship, world, deltaSeconds, events); } var controllerEvent = UpdateControllerTask(ship, world, deltaSeconds); AdvanceControlState(ship, world, controllerEvent); ship.Velocity = ship.Position.Subtract(previousPosition).Divide(deltaSeconds); TrackHistory(ship); EmitShipStateEvents(ship, previousState, previousBehavior, previousTask, controllerEvent, events); } SyncSpatialState(world); world.GeneratedAtUtc = nowUtc; return new WorldDelta( sequence, world.TickIntervalMs, world.GeneratedAtUtc, false, events, BuildSpatialNodeDeltas(world), BuildLocalBubbleDeltas(world), BuildNodeDeltas(world), BuildStationDeltas(world), BuildClaimDeltas(world), BuildConstructionSiteDeltas(world), BuildMarketOrderDeltas(world), BuildPolicyDeltas(world), BuildShipDeltas(world), BuildFactionDeltas(world)); } private delegate void WorldUpdateStepAction( SimulationEngine engine, SimulationWorld world, float deltaSeconds, DateTimeOffset nowUtc, List events); private delegate void ShipUpdateStepAction( SimulationEngine engine, ShipRuntime ship, SimulationWorld world, float deltaSeconds, List events); private sealed record WorldUpdateStep(WorldUpdateStepAction Execute); private sealed record ShipUpdateStep(ShipUpdateStepAction Execute); }