Organize simulation systems and balancing profiles
This commit is contained in:
@@ -1,74 +1,78 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation.Difficulties;
|
||||
|
||||
public static class Balancing
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public abstract class Balancing
|
||||
{
|
||||
public static int MinHazardValue => 0;
|
||||
public static int MaxHazardValue => 10;
|
||||
public static int DefaultHazardStability => 10;
|
||||
public static int DefaultCellIntegrity => 10;
|
||||
public static int DefaultActionsPerTurn => 3;
|
||||
public static int DefaultCoreHeat => 5;
|
||||
public static int DefaultFacilityStability => 10;
|
||||
public static int DefaultPower => 5;
|
||||
public static int DefaultCooling => 0;
|
||||
public static int FirstGridCoordinate => 0;
|
||||
public static int NeighborDistance => 1;
|
||||
public static int CurrentForecastTurn => 0;
|
||||
public static int MinimumLevelSize => 4;
|
||||
public static int DefaultLevelWidth => 16;
|
||||
public static int DefaultLevelHeight => 12;
|
||||
public static int DefaultRobotCoordinate => 1;
|
||||
public static int DefaultPipeFlow => 4;
|
||||
public static int DefaultPipePressure => 4;
|
||||
public static int DefaultPressurePipeFlow => 5;
|
||||
public static int DefaultPressurePipePressure => 6;
|
||||
public static int DefaultEditedPipeIntegrity => 8;
|
||||
public static int MinimumLeakRate => 1;
|
||||
public static int DamagedPipeIntegrity => 4;
|
||||
public static int RepairedLeakRate => 0;
|
||||
public static int RepairedElectricalCharge => 0;
|
||||
public static int HeatToolIncrease => 2;
|
||||
public static int FireToolMinimumHeat => 7;
|
||||
public static int FireToolMinimumSmoke => 3;
|
||||
public static int MaxForecastStepCount => 12;
|
||||
public static int TurnIncrement => 1;
|
||||
public static int OverpressureThreshold => 7;
|
||||
public static int HeatIntegrityDamageThreshold => 10;
|
||||
public static int PipeFireIntegrityDamage => 1;
|
||||
public static int FireStabilityDamage => 1;
|
||||
public static int BurstLeakRate => 3;
|
||||
public static int BrokenPipeFlow => 0;
|
||||
public static int ElectrifiedCoolantPoolingThreshold => 3;
|
||||
public static int ElectricalChargeIncrease => 2;
|
||||
public static int FuelVaporFireThreshold => 4;
|
||||
public static int LiquidFuelFireThreshold => 6;
|
||||
public static int HeatIgnitionThreshold => 8;
|
||||
public static int ElectricalIgnitionThreshold => 4;
|
||||
public static int FireHeatIncrease => 2;
|
||||
public static int FireSmokeIncrease => 2;
|
||||
public static int FireLiquidFuelConsumption => 1;
|
||||
public static int FireFuelVaporConsumption => 1;
|
||||
public static int SmokeDecay => 1;
|
||||
public static int PressurizedFuelLeakPressureThreshold => 7;
|
||||
public static int PassiveFuelVaporHeatOffset => 3;
|
||||
public static int PassiveFuelVaporDivisor => 3;
|
||||
public static int MinimumCoolantHeatReduction => 1;
|
||||
public static int CoolantHeatReductionDivisor => 2;
|
||||
public static int CoolantSteamHeatThreshold => 7;
|
||||
public static int CoolantSteamSmokeIncrease => 2;
|
||||
public static int PressureLeakSmokeThreshold => 8;
|
||||
public static int PressureLeakSmokeIncrease => 1;
|
||||
public static int GeneratorHeatIncrease => 1;
|
||||
public static int CoolingPumpHeatReduction => 2;
|
||||
public static int ReactorHeatIncrease => 1;
|
||||
public static int SmokeSpreadThreshold => 6;
|
||||
public static int SmokeSpreadIncrease => 1;
|
||||
public static int CriticalCellStabilityThreshold => 3;
|
||||
public static int MeltdownCoreHeatThreshold => 10;
|
||||
public static int StabilityCollapseThreshold => 0;
|
||||
public static int GeneratorPowerOutput => 3;
|
||||
public static int CoolingPumpOutput => 3;
|
||||
public static int ReactorReadyPowerThreshold => 3;
|
||||
public static int ReactorReadyCoolingThreshold => 3;
|
||||
public static int ReactorReadyCoreHeatThreshold => 8;
|
||||
public static Balancing Current { get; set; } = new NormalBalancing();
|
||||
|
||||
public abstract int MinHazardValue { get; }
|
||||
public abstract int MaxHazardValue { get; }
|
||||
public abstract int DefaultHazardStability { get; }
|
||||
public abstract int DefaultCellIntegrity { get; }
|
||||
public abstract int DefaultActionsPerTurn { get; }
|
||||
public abstract int DefaultCoreHeat { get; }
|
||||
public abstract int DefaultFacilityStability { get; }
|
||||
public abstract int DefaultPower { get; }
|
||||
public abstract int DefaultCooling { get; }
|
||||
public abstract int FirstGridCoordinate { get; }
|
||||
public abstract int NeighborDistance { get; }
|
||||
public abstract int CurrentForecastTurn { get; }
|
||||
public abstract int MinimumLevelSize { get; }
|
||||
public abstract int DefaultLevelWidth { get; }
|
||||
public abstract int DefaultLevelHeight { get; }
|
||||
public abstract int DefaultRobotCoordinate { get; }
|
||||
public abstract int DefaultPipeFlow { get; }
|
||||
public abstract int DefaultPipePressure { get; }
|
||||
public abstract int DefaultPressurePipeFlow { get; }
|
||||
public abstract int DefaultPressurePipePressure { get; }
|
||||
public abstract int DefaultEditedPipeIntegrity { get; }
|
||||
public abstract int MinimumLeakRate { get; }
|
||||
public abstract int DamagedPipeIntegrity { get; }
|
||||
public abstract int RepairedLeakRate { get; }
|
||||
public abstract int RepairedElectricalCharge { get; }
|
||||
public abstract int HeatToolIncrease { get; }
|
||||
public abstract int FireToolMinimumHeat { get; }
|
||||
public abstract int FireToolMinimumSmoke { get; }
|
||||
public abstract int MaxForecastStepCount { get; }
|
||||
public abstract int TurnIncrement { get; }
|
||||
public abstract int OverpressureThreshold { get; }
|
||||
public abstract int HeatIntegrityDamageThreshold { get; }
|
||||
public abstract int PipeFireIntegrityDamage { get; }
|
||||
public abstract int FireStabilityDamage { get; }
|
||||
public abstract int BurstLeakRate { get; }
|
||||
public abstract int BrokenPipeFlow { get; }
|
||||
public abstract int ElectrifiedCoolantPoolingThreshold { get; }
|
||||
public abstract int ElectricalChargeIncrease { get; }
|
||||
public abstract int FuelVaporFireThreshold { get; }
|
||||
public abstract int LiquidFuelFireThreshold { get; }
|
||||
public abstract int HeatIgnitionThreshold { get; }
|
||||
public abstract int ElectricalIgnitionThreshold { get; }
|
||||
public abstract int FireHeatIncrease { get; }
|
||||
public abstract int FireSmokeIncrease { get; }
|
||||
public abstract int FireLiquidFuelConsumption { get; }
|
||||
public abstract int FireFuelVaporConsumption { get; }
|
||||
public abstract int SmokeDecay { get; }
|
||||
public abstract int PressurizedFuelLeakPressureThreshold { get; }
|
||||
public abstract int PassiveFuelVaporHeatOffset { get; }
|
||||
public abstract int PassiveFuelVaporDivisor { get; }
|
||||
public abstract int MinimumCoolantHeatReduction { get; }
|
||||
public abstract int CoolantHeatReductionDivisor { get; }
|
||||
public abstract int CoolantSteamHeatThreshold { get; }
|
||||
public abstract int CoolantSteamSmokeIncrease { get; }
|
||||
public abstract int PressureLeakSmokeThreshold { get; }
|
||||
public abstract int PressureLeakSmokeIncrease { get; }
|
||||
public abstract int GeneratorHeatIncrease { get; }
|
||||
public abstract int CoolingPumpHeatReduction { get; }
|
||||
public abstract int ReactorHeatIncrease { get; }
|
||||
public abstract int SmokeSpreadThreshold { get; }
|
||||
public abstract int SmokeSpreadIncrease { get; }
|
||||
public abstract int CriticalCellStabilityThreshold { get; }
|
||||
public abstract int MeltdownCoreHeatThreshold { get; }
|
||||
public abstract int StabilityCollapseThreshold { get; }
|
||||
public abstract int GeneratorPowerOutput { get; }
|
||||
public abstract int CoolingPumpOutput { get; }
|
||||
public abstract int ReactorReadyPowerThreshold { get; }
|
||||
public abstract int ReactorReadyCoolingThreshold { get; }
|
||||
public abstract int ReactorReadyCoreHeatThreshold { get; }
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class CellIntegrityEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var integrity = cell.Integrity;
|
||||
var hazards = cell.Hazards;
|
||||
|
||||
if (cell is { HasPipe: true } && cell.Pressure > Balancing.OverpressureThreshold)
|
||||
integrity -= cell.Pressure - Balancing.OverpressureThreshold;
|
||||
|
||||
if (hazards.Heat >= Balancing.HeatIntegrityDamageThreshold || hazards.Fire)
|
||||
{
|
||||
integrity -= cell.HasPipe ? Balancing.PipeFireIntegrityDamage : Balancing.MinHazardValue;
|
||||
hazards = hazards with { Stability = hazards.Stability - Balancing.FireStabilityDamage };
|
||||
}
|
||||
|
||||
cell = cell with {
|
||||
Integrity = Rules.Clamp(integrity),
|
||||
Hazards = hazards.Clamp()
|
||||
};
|
||||
|
||||
if (integrity > Balancing.MinHazardValue || !cell.HasPipe)
|
||||
return cell;
|
||||
|
||||
return cell with {
|
||||
LeakRate = Math.Max(cell.LeakRate, Balancing.BurstLeakRate),
|
||||
Flow = Balancing.BrokenPipeFlow,
|
||||
PipeOpen = false
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Difficulties;
|
||||
|
||||
public class NormalBalancing : Balancing
|
||||
{
|
||||
public override int MinHazardValue => 0;
|
||||
public override int MaxHazardValue => 10;
|
||||
public override int DefaultHazardStability => 10;
|
||||
public override int DefaultCellIntegrity => 10;
|
||||
public override int DefaultActionsPerTurn => 3;
|
||||
public override int DefaultCoreHeat => 5;
|
||||
public override int DefaultFacilityStability => 10;
|
||||
public override int DefaultPower => 5;
|
||||
public override int DefaultCooling => 0;
|
||||
public override int FirstGridCoordinate => 0;
|
||||
public override int NeighborDistance => 1;
|
||||
public override int CurrentForecastTurn => 0;
|
||||
public override int MinimumLevelSize => 4;
|
||||
public override int DefaultLevelWidth => 16;
|
||||
public override int DefaultLevelHeight => 12;
|
||||
public override int DefaultRobotCoordinate => 1;
|
||||
public override int DefaultPipeFlow => 4;
|
||||
public override int DefaultPipePressure => 4;
|
||||
public override int DefaultPressurePipeFlow => 5;
|
||||
public override int DefaultPressurePipePressure => 6;
|
||||
public override int DefaultEditedPipeIntegrity => 8;
|
||||
public override int MinimumLeakRate => 1;
|
||||
public override int DamagedPipeIntegrity => 4;
|
||||
public override int RepairedLeakRate => 0;
|
||||
public override int RepairedElectricalCharge => 0;
|
||||
public override int HeatToolIncrease => 2;
|
||||
public override int FireToolMinimumHeat => 7;
|
||||
public override int FireToolMinimumSmoke => 3;
|
||||
public override int MaxForecastStepCount => 12;
|
||||
public override int TurnIncrement => 1;
|
||||
public override int OverpressureThreshold => 7;
|
||||
public override int HeatIntegrityDamageThreshold => 10;
|
||||
public override int PipeFireIntegrityDamage => 1;
|
||||
public override int FireStabilityDamage => 1;
|
||||
public override int BurstLeakRate => 3;
|
||||
public override int BrokenPipeFlow => 0;
|
||||
public override int ElectrifiedCoolantPoolingThreshold => 3;
|
||||
public override int ElectricalChargeIncrease => 2;
|
||||
public override int FuelVaporFireThreshold => 4;
|
||||
public override int LiquidFuelFireThreshold => 6;
|
||||
public override int HeatIgnitionThreshold => 8;
|
||||
public override int ElectricalIgnitionThreshold => 4;
|
||||
public override int FireHeatIncrease => 2;
|
||||
public override int FireSmokeIncrease => 2;
|
||||
public override int FireLiquidFuelConsumption => 1;
|
||||
public override int FireFuelVaporConsumption => 1;
|
||||
public override int SmokeDecay => 1;
|
||||
public override int PressurizedFuelLeakPressureThreshold => 7;
|
||||
public override int PassiveFuelVaporHeatOffset => 3;
|
||||
public override int PassiveFuelVaporDivisor => 3;
|
||||
public override int MinimumCoolantHeatReduction => 1;
|
||||
public override int CoolantHeatReductionDivisor => 2;
|
||||
public override int CoolantSteamHeatThreshold => 7;
|
||||
public override int CoolantSteamSmokeIncrease => 2;
|
||||
public override int PressureLeakSmokeThreshold => 8;
|
||||
public override int PressureLeakSmokeIncrease => 1;
|
||||
public override int GeneratorHeatIncrease => 1;
|
||||
public override int CoolingPumpHeatReduction => 2;
|
||||
public override int ReactorHeatIncrease => 1;
|
||||
public override int SmokeSpreadThreshold => 6;
|
||||
public override int SmokeSpreadIncrease => 1;
|
||||
public override int CriticalCellStabilityThreshold => 3;
|
||||
public override int MeltdownCoreHeatThreshold => 10;
|
||||
public override int StabilityCollapseThreshold => 0;
|
||||
public override int GeneratorPowerOutput => 3;
|
||||
public override int CoolingPumpOutput => 3;
|
||||
public override int ReactorReadyPowerThreshold => 3;
|
||||
public override int ReactorReadyCoolingThreshold => 3;
|
||||
public override int ReactorReadyCoreHeatThreshold => 8;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class CellIntegrityEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var integrity = cell.Integrity;
|
||||
var hazards = cell.Hazards;
|
||||
|
||||
if (cell is { HasPipe: true } && cell.Pressure > Balancing.Current.OverpressureThreshold)
|
||||
integrity -= cell.Pressure - Balancing.Current.OverpressureThreshold;
|
||||
|
||||
if (hazards.Heat >= Balancing.Current.HeatIntegrityDamageThreshold || hazards.Fire)
|
||||
{
|
||||
integrity -= cell.HasPipe ? Balancing.Current.PipeFireIntegrityDamage : Balancing.Current.MinHazardValue;
|
||||
hazards = hazards with { Stability = hazards.Stability - Balancing.Current.FireStabilityDamage };
|
||||
}
|
||||
|
||||
cell = cell with {
|
||||
Integrity = Rules.Clamp(integrity),
|
||||
Hazards = hazards.Clamp()
|
||||
};
|
||||
|
||||
if (integrity > Balancing.Current.MinHazardValue || !cell.HasPipe)
|
||||
return cell;
|
||||
|
||||
return cell with {
|
||||
LeakRate = Math.Max(cell.LeakRate, Balancing.Current.BurstLeakRate),
|
||||
Flow = Balancing.Current.BrokenPipeFlow,
|
||||
PipeOpen = false
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class FireAndElectricalHazardEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var hazards = cell.Hazards;
|
||||
if (hazards.CoolantPooling >= Balancing.Current.ElectrifiedCoolantPoolingThreshold && cell.Powered)
|
||||
hazards = hazards with { ElectricalCharge = hazards.ElectricalCharge + Balancing.Current.ElectricalChargeIncrease };
|
||||
|
||||
var hasFuel = hazards.FuelVapor >= Balancing.Current.FuelVaporFireThreshold || hazards.LiquidFuel >= Balancing.Current.LiquidFuelFireThreshold;
|
||||
var hasIgnition = hazards.Heat >= Balancing.Current.HeatIgnitionThreshold || hazards.ElectricalCharge >= Balancing.Current.ElectricalIgnitionThreshold || cell is { Kind: ECellKind.Generator, Powered: true };
|
||||
if ((hasFuel && hasIgnition) || hazards.Fire)
|
||||
{
|
||||
hazards = hazards with {
|
||||
Fire = hasFuel || hazards.Fire,
|
||||
Heat = hazards.Heat + Balancing.Current.FireHeatIncrease,
|
||||
Smoke = hazards.Smoke + Balancing.Current.FireSmokeIncrease,
|
||||
LiquidFuel = Math.Max(Balancing.Current.MinHazardValue, hazards.LiquidFuel - Balancing.Current.FireLiquidFuelConsumption),
|
||||
FuelVapor = Math.Max(Balancing.Current.MinHazardValue, hazards.FuelVapor - Balancing.Current.FireFuelVaporConsumption)
|
||||
};
|
||||
}
|
||||
else if (hazards.Smoke > Balancing.Current.MinHazardValue)
|
||||
hazards = hazards with { Smoke = hazards.Smoke - Balancing.Current.SmokeDecay };
|
||||
|
||||
return cell with { Hazards = hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public interface IAreaSimulationEffect
|
||||
{
|
||||
@@ -0,0 +1,8 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public interface ISimulationEffect
|
||||
{
|
||||
CellState Apply(CellState cell);
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class MachineEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var hazards = cell.Kind switch {
|
||||
ECellKind.Generator when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.GeneratorHeatIncrease },
|
||||
ECellKind.CoolingPump when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat - Balancing.CoolingPumpHeatReduction },
|
||||
ECellKind.Reactor => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.ReactorHeatIncrease },
|
||||
ECellKind.Generator when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.Current.GeneratorHeatIncrease },
|
||||
ECellKind.CoolingPump when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat - Balancing.Current.CoolingPumpHeatReduction },
|
||||
ECellKind.Reactor => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.Current.ReactorHeatIncrease },
|
||||
_ => cell.Hazards
|
||||
};
|
||||
|
||||
28
src/ReactorMaintenance.Simulation/Effects/PipeLeakEffect.cs
Normal file
28
src/ReactorMaintenance.Simulation/Effects/PipeLeakEffect.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class PipeLeakEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
if (!cell.HasPipe || cell.LeakRate <= Balancing.Current.MinHazardValue)
|
||||
return cell;
|
||||
|
||||
var hazards = cell.Pipe switch {
|
||||
EPipeMedium.Fuel => cell.Hazards with {
|
||||
LiquidFuel = cell.Hazards.LiquidFuel + cell.LeakRate,
|
||||
FuelVapor = cell.Hazards.FuelVapor + (cell.Pressure >= Balancing.Current.PressurizedFuelLeakPressureThreshold ? cell.LeakRate : Math.Max(Balancing.Current.MinHazardValue, cell.Hazards.Heat - Balancing.Current.PassiveFuelVaporHeatOffset) / Balancing.Current.PassiveFuelVaporDivisor)
|
||||
},
|
||||
EPipeMedium.Coolant => cell.Hazards with {
|
||||
CoolantPooling = cell.Hazards.CoolantPooling + cell.LeakRate,
|
||||
Heat = cell.Hazards.Heat - Math.Max(Balancing.Current.MinimumCoolantHeatReduction, cell.LeakRate / Balancing.Current.CoolantHeatReductionDivisor),
|
||||
Smoke = cell.Hazards.Smoke + (cell.Hazards.Heat >= Balancing.Current.CoolantSteamHeatThreshold ? Balancing.Current.CoolantSteamSmokeIncrease : Balancing.Current.MinHazardValue)
|
||||
},
|
||||
EPipeMedium.Pressure => cell.Hazards with { Smoke = cell.Hazards.Smoke + (cell.Pressure >= Balancing.Current.PressureLeakSmokeThreshold ? Balancing.Current.PressureLeakSmokeIncrease : Balancing.Current.MinHazardValue) },
|
||||
_ => cell.Hazards
|
||||
};
|
||||
|
||||
return cell with { Hazards = hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class SmokeSpreadEffect : IAreaSimulationEffect
|
||||
{
|
||||
public CellState[] Apply(LevelState level, CellState[] cells)
|
||||
{
|
||||
var next = cells.ToArray();
|
||||
for (var y = Balancing.FirstGridCoordinate; y < level.Height; y++)
|
||||
for (var y = Balancing.Current.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
for (var x = Balancing.FirstGridCoordinate; x < level.Width; x++)
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < level.Width; x++)
|
||||
{
|
||||
var position = new GridPosition(x, y);
|
||||
var cell = cells[level.Index(position)];
|
||||
if (cell.Hazards.Smoke < Balancing.SmokeSpreadThreshold)
|
||||
if (cell.Hazards.Smoke < Balancing.Current.SmokeSpreadThreshold)
|
||||
continue;
|
||||
|
||||
SpreadToNeighbors(level, next, position);
|
||||
@@ -29,7 +31,7 @@ public sealed class SmokeSpreadEffect : IAreaSimulationEffect
|
||||
if (!neighborCell.IsWalkable || neighborCell.DoorLocked)
|
||||
continue;
|
||||
|
||||
next[level.Index(neighbor)] = neighborCell with { Hazards = neighborCell.Hazards with { Smoke = Rules.Clamp(neighborCell.Hazards.Smoke + Balancing.SmokeSpreadIncrease) } };
|
||||
next[level.Index(neighbor)] = neighborCell with { Hazards = neighborCell.Hazards with { Smoke = Rules.Clamp(neighborCell.Hazards.Smoke + Balancing.Current.SmokeSpreadIncrease) } };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class FireAndElectricalHazardEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var hazards = cell.Hazards;
|
||||
if (hazards.CoolantPooling >= Balancing.ElectrifiedCoolantPoolingThreshold && cell.Powered)
|
||||
hazards = hazards with { ElectricalCharge = hazards.ElectricalCharge + Balancing.ElectricalChargeIncrease };
|
||||
|
||||
var hasFuel = hazards.FuelVapor >= Balancing.FuelVaporFireThreshold || hazards.LiquidFuel >= Balancing.LiquidFuelFireThreshold;
|
||||
var hasIgnition = hazards.Heat >= Balancing.HeatIgnitionThreshold || hazards.ElectricalCharge >= Balancing.ElectricalIgnitionThreshold || cell is { Kind: ECellKind.Generator, Powered: true };
|
||||
if ((hasFuel && hasIgnition) || hazards.Fire)
|
||||
{
|
||||
hazards = hazards with {
|
||||
Fire = hasFuel || hazards.Fire,
|
||||
Heat = hazards.Heat + Balancing.FireHeatIncrease,
|
||||
Smoke = hazards.Smoke + Balancing.FireSmokeIncrease,
|
||||
LiquidFuel = Math.Max(Balancing.MinHazardValue, hazards.LiquidFuel - Balancing.FireLiquidFuelConsumption),
|
||||
FuelVapor = Math.Max(Balancing.MinHazardValue, hazards.FuelVapor - Balancing.FireFuelVaporConsumption)
|
||||
};
|
||||
}
|
||||
else if (hazards.Smoke > Balancing.MinHazardValue)
|
||||
hazards = hazards with { Smoke = hazards.Smoke - Balancing.SmokeDecay };
|
||||
|
||||
return cell with { Hazards = hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public abstract class Hazard
|
||||
{
|
||||
20
src/ReactorMaintenance.Simulation/Hazards/IgnitionHazard.cs
Normal file
20
src/ReactorMaintenance.Simulation/Hazards/IgnitionHazard.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class IgnitionHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
for (var y = Balancing.Current.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < level.Width; x++)
|
||||
{
|
||||
var position = new GridPosition(x, y);
|
||||
var cell = level.GetCell(position);
|
||||
if (cell.Hazards.Fire)
|
||||
yield return new(EFailureKind.Ignition, position, turns, turns == Balancing.Current.TurnIncrement ? $"FUEL IGNITION PREDICTED AT {x},{y} NEXT TURN" : $"FUEL IGNITION PREDICTED AT {x},{y} IN {turns} TURNS");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class MeltdownHazard : Hazard
|
||||
{
|
||||
@@ -1,16 +1,18 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class PipeBurstHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
for (var y = Balancing.FirstGridCoordinate; y < level.Height; y++)
|
||||
for (var y = Balancing.Current.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
for (var x = Balancing.FirstGridCoordinate; x < level.Width; x++)
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < level.Width; x++)
|
||||
{
|
||||
var position = new GridPosition(x, y);
|
||||
var cell = level.GetCell(position);
|
||||
if (cell is { HasPipe: true, PipeOpen: false } && cell.Flow == Balancing.BrokenPipeFlow && cell.LeakRate >= Balancing.BurstLeakRate)
|
||||
if (cell is { HasPipe: true, PipeOpen: false } && cell.Flow == Balancing.Current.BrokenPipeFlow && cell.LeakRate >= Balancing.Current.BurstLeakRate)
|
||||
yield return new(EFailureKind.PipeBurst, position, turns, $"PIPE BURST PREDICTED AT {x},{y} IN {turns} TURNS");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class StabilityCollapseHazard : Hazard
|
||||
{
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public interface ISimulationEffect
|
||||
{
|
||||
CellState Apply(CellState cell);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class IgnitionHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
for (var y = Balancing.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
for (var x = Balancing.FirstGridCoordinate; x < level.Width; x++)
|
||||
{
|
||||
var position = new GridPosition(x, y);
|
||||
var cell = level.GetCell(position);
|
||||
if (cell.Hazards.Fire)
|
||||
yield return new(EFailureKind.Ignition, position, turns, turns == Balancing.TurnIncrement ? $"FUEL IGNITION PREDICTED AT {x},{y} NEXT TURN" : $"FUEL IGNITION PREDICTED AT {x},{y} IN {turns} TURNS");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,43 +58,43 @@ public static class LevelEditor
|
||||
},
|
||||
EEditorTool.CoolantPipe => cell with {
|
||||
Pipe = EPipeMedium.Coolant,
|
||||
Flow = Balancing.DefaultPipeFlow,
|
||||
Pressure = Balancing.DefaultPipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.DefaultEditedPipeIntegrity),
|
||||
Flow = Balancing.Current.DefaultPipeFlow,
|
||||
Pressure = Balancing.Current.DefaultPipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.Current.DefaultEditedPipeIntegrity),
|
||||
PipeOpen = true
|
||||
},
|
||||
EEditorTool.FuelPipe => cell with {
|
||||
Pipe = EPipeMedium.Fuel,
|
||||
Flow = Balancing.DefaultPipeFlow,
|
||||
Pressure = Balancing.DefaultPipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.DefaultEditedPipeIntegrity),
|
||||
Flow = Balancing.Current.DefaultPipeFlow,
|
||||
Pressure = Balancing.Current.DefaultPipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.Current.DefaultEditedPipeIntegrity),
|
||||
PipeOpen = true
|
||||
},
|
||||
EEditorTool.PressurePipe => cell with {
|
||||
Pipe = EPipeMedium.Pressure,
|
||||
Flow = Balancing.DefaultPressurePipeFlow,
|
||||
Pressure = Balancing.DefaultPressurePipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.DefaultEditedPipeIntegrity),
|
||||
Flow = Balancing.Current.DefaultPressurePipeFlow,
|
||||
Pressure = Balancing.Current.DefaultPressurePipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.Current.DefaultEditedPipeIntegrity),
|
||||
PipeOpen = true
|
||||
},
|
||||
EEditorTool.Leak => cell with {
|
||||
LeakRate = Math.Max(Balancing.MinimumLeakRate, cell.LeakRate),
|
||||
Integrity = Math.Min(cell.Integrity, Balancing.DamagedPipeIntegrity)
|
||||
LeakRate = Math.Max(Balancing.Current.MinimumLeakRate, cell.LeakRate),
|
||||
Integrity = Math.Min(cell.Integrity, Balancing.Current.DamagedPipeIntegrity)
|
||||
},
|
||||
EEditorTool.Repair => cell with {
|
||||
LeakRate = Balancing.RepairedLeakRate,
|
||||
Integrity = Balancing.DefaultCellIntegrity,
|
||||
LeakRate = Balancing.Current.RepairedLeakRate,
|
||||
Integrity = Balancing.Current.DefaultCellIntegrity,
|
||||
Hazards = cell.Hazards with {
|
||||
Fire = false,
|
||||
ElectricalCharge = Balancing.RepairedElectricalCharge
|
||||
ElectricalCharge = Balancing.Current.RepairedElectricalCharge
|
||||
}
|
||||
},
|
||||
EEditorTool.Heat => cell with { Hazards = cell.Hazards with { Heat = Rules.Clamp(cell.Hazards.Heat + Balancing.HeatToolIncrease) } },
|
||||
EEditorTool.Heat => cell with { Hazards = cell.Hazards with { Heat = Rules.Clamp(cell.Hazards.Heat + Balancing.Current.HeatToolIncrease) } },
|
||||
EEditorTool.Fire => cell with {
|
||||
Hazards = cell.Hazards with {
|
||||
Fire = !cell.Hazards.Fire,
|
||||
Heat = Math.Max(cell.Hazards.Heat, Balancing.FireToolMinimumHeat),
|
||||
Smoke = Math.Max(cell.Hazards.Smoke, Balancing.FireToolMinimumSmoke)
|
||||
Heat = Math.Max(cell.Hazards.Heat, Balancing.Current.FireToolMinimumHeat),
|
||||
Smoke = Math.Max(cell.Hazards.Smoke, Balancing.Current.FireToolMinimumSmoke)
|
||||
}
|
||||
},
|
||||
_ => cell
|
||||
|
||||
@@ -34,10 +34,10 @@ public sealed record GridPosition(int X, int Y)
|
||||
{
|
||||
public IEnumerable<GridPosition> Neighbors()
|
||||
{
|
||||
yield return new(X - Balancing.NeighborDistance, Y);
|
||||
yield return new(X + Balancing.NeighborDistance, Y);
|
||||
yield return new(X, Y - Balancing.NeighborDistance);
|
||||
yield return new(X, Y + Balancing.NeighborDistance);
|
||||
yield return new(X - Balancing.Current.NeighborDistance, Y);
|
||||
yield return new(X + Balancing.Current.NeighborDistance, Y);
|
||||
yield return new(X, Y - Balancing.Current.NeighborDistance);
|
||||
yield return new(X, Y + Balancing.Current.NeighborDistance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public sealed record HazardState
|
||||
public int LiquidFuel { get; init; }
|
||||
public int CoolantPooling { get; init; }
|
||||
public int ElectricalCharge { get; init; }
|
||||
public int Stability { get; init; } = Balancing.DefaultHazardStability;
|
||||
public int Stability { get; init; } = Balancing.Current.DefaultHazardStability;
|
||||
public bool Fire { get; init; }
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public sealed record CellState
|
||||
public EPipeMedium Pipe { get; init; }
|
||||
public int Flow { get; init; }
|
||||
public int Pressure { get; init; }
|
||||
public int Integrity { get; init; } = Balancing.DefaultCellIntegrity;
|
||||
public int Integrity { get; init; } = Balancing.Current.DefaultCellIntegrity;
|
||||
public int LeakRate { get; init; }
|
||||
public bool PipeOpen { get; init; } = true;
|
||||
public bool Powered { get; init; }
|
||||
@@ -85,11 +85,11 @@ public sealed record CellState
|
||||
public sealed record GlobalState
|
||||
{
|
||||
public int Turn { get; init; }
|
||||
public int ActionsPerTurn { get; init; } = Balancing.DefaultActionsPerTurn;
|
||||
public int CoreHeat { get; init; } = Balancing.DefaultCoreHeat;
|
||||
public int FacilityStability { get; init; } = Balancing.DefaultFacilityStability;
|
||||
public int Power { get; init; } = Balancing.DefaultPower;
|
||||
public int Cooling { get; init; } = Balancing.DefaultCooling;
|
||||
public int ActionsPerTurn { get; init; } = Balancing.Current.DefaultActionsPerTurn;
|
||||
public int CoreHeat { get; init; } = Balancing.Current.DefaultCoreHeat;
|
||||
public int FacilityStability { get; init; } = Balancing.Current.DefaultFacilityStability;
|
||||
public int Power { get; init; } = Balancing.Current.DefaultPower;
|
||||
public int Cooling { get; init; } = Balancing.Current.DefaultCooling;
|
||||
public bool ReactorActivated { get; init; }
|
||||
public bool Lost { get; init; }
|
||||
public string Status { get; init; } = "STABILIZE SYSTEMS";
|
||||
@@ -101,15 +101,15 @@ public sealed record LevelState
|
||||
{
|
||||
public static LevelState Create(string name, int width, int height)
|
||||
{
|
||||
if (width < Balancing.MinimumLevelSize || height < Balancing.MinimumLevelSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(width), $"Levels must be at least {Balancing.MinimumLevelSize}x{Balancing.MinimumLevelSize}.");
|
||||
if (width < Balancing.Current.MinimumLevelSize || height < Balancing.Current.MinimumLevelSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(width), $"Levels must be at least {Balancing.Current.MinimumLevelSize}x{Balancing.Current.MinimumLevelSize}.");
|
||||
|
||||
var cells = CreateCells(width, height);
|
||||
for (var y = Balancing.FirstGridCoordinate; y < height; y++)
|
||||
for (var y = Balancing.Current.FirstGridCoordinate; y < height; y++)
|
||||
{
|
||||
for (var x = Balancing.FirstGridCoordinate; x < width; x++)
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < width; x++)
|
||||
{
|
||||
if (x == Balancing.FirstGridCoordinate || y == Balancing.FirstGridCoordinate || x == width - Balancing.NeighborDistance || y == height - Balancing.NeighborDistance)
|
||||
if (x == Balancing.Current.FirstGridCoordinate || y == Balancing.Current.FirstGridCoordinate || x == width - Balancing.Current.NeighborDistance || y == height - Balancing.Current.NeighborDistance)
|
||||
cells[y * width + x] = cells[y * width + x] with { Kind = ECellKind.Wall };
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public sealed record LevelState
|
||||
Width = width,
|
||||
Height = height,
|
||||
Cells = cells,
|
||||
Robot = new(Balancing.DefaultRobotCoordinate, Balancing.DefaultRobotCoordinate)
|
||||
Robot = new(Balancing.Current.DefaultRobotCoordinate, Balancing.Current.DefaultRobotCoordinate)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public sealed record LevelState
|
||||
|
||||
public bool InBounds(GridPosition position)
|
||||
{
|
||||
return position.X >= Balancing.FirstGridCoordinate && position.Y >= Balancing.FirstGridCoordinate && position.X < Width && position.Y < Height;
|
||||
return position.X >= Balancing.Current.FirstGridCoordinate && position.Y >= Balancing.Current.FirstGridCoordinate && position.X < Width && position.Y < Height;
|
||||
}
|
||||
|
||||
public int Index(GridPosition position)
|
||||
@@ -155,14 +155,14 @@ public sealed record LevelState
|
||||
|
||||
private static CellState[] CreateCells(int width, int height)
|
||||
{
|
||||
return Enumerable.Range(Balancing.FirstGridCoordinate, width * height).Select(_ => new CellState()).ToArray();
|
||||
return Enumerable.Range(Balancing.Current.FirstGridCoordinate, width * height).Select(_ => new CellState()).ToArray();
|
||||
}
|
||||
|
||||
public string Name { get; init; } = "New Reactor";
|
||||
public int Width { get; init; } = Balancing.DefaultLevelWidth;
|
||||
public int Height { get; init; } = Balancing.DefaultLevelHeight;
|
||||
public CellState[] Cells { get; init; } = CreateCells(Balancing.DefaultLevelWidth, Balancing.DefaultLevelHeight);
|
||||
public GridPosition Robot { get; init; } = new(Balancing.DefaultRobotCoordinate, Balancing.DefaultRobotCoordinate);
|
||||
public int Width { get; init; } = Balancing.Current.DefaultLevelWidth;
|
||||
public int Height { get; init; } = Balancing.Current.DefaultLevelHeight;
|
||||
public CellState[] Cells { get; init; } = CreateCells(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
|
||||
public GridPosition Robot { get; init; } = new(Balancing.Current.DefaultRobotCoordinate, Balancing.Current.DefaultRobotCoordinate);
|
||||
public GlobalState Global { get; init; } = new();
|
||||
public IReadOnlyList<Forecast> Forecasts { get; init; } = Array.Empty<Forecast>();
|
||||
}
|
||||
@@ -171,6 +171,6 @@ internal static class Rules
|
||||
{
|
||||
public static int Clamp(int value)
|
||||
{
|
||||
return Math.Clamp(value, Balancing.MinHazardValue, Balancing.MaxHazardValue);
|
||||
return Math.Clamp(value, Balancing.Current.MinHazardValue, Balancing.Current.MaxHazardValue);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class PipeLeakEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
if (!cell.HasPipe || cell.LeakRate <= Balancing.MinHazardValue)
|
||||
return cell;
|
||||
|
||||
var hazards = cell.Pipe switch {
|
||||
EPipeMedium.Fuel => cell.Hazards with {
|
||||
LiquidFuel = cell.Hazards.LiquidFuel + cell.LeakRate,
|
||||
FuelVapor = cell.Hazards.FuelVapor + (cell.Pressure >= Balancing.PressurizedFuelLeakPressureThreshold ? cell.LeakRate : Math.Max(Balancing.MinHazardValue, cell.Hazards.Heat - Balancing.PassiveFuelVaporHeatOffset) / Balancing.PassiveFuelVaporDivisor)
|
||||
},
|
||||
EPipeMedium.Coolant => cell.Hazards with {
|
||||
CoolantPooling = cell.Hazards.CoolantPooling + cell.LeakRate,
|
||||
Heat = cell.Hazards.Heat - Math.Max(Balancing.MinimumCoolantHeatReduction, cell.LeakRate / Balancing.CoolantHeatReductionDivisor),
|
||||
Smoke = cell.Hazards.Smoke + (cell.Hazards.Heat >= Balancing.CoolantSteamHeatThreshold ? Balancing.CoolantSteamSmokeIncrease : Balancing.MinHazardValue)
|
||||
},
|
||||
EPipeMedium.Pressure => cell.Hazards with { Smoke = cell.Hazards.Smoke + (cell.Pressure >= Balancing.PressureLeakSmokeThreshold ? Balancing.PressureLeakSmokeIncrease : Balancing.MinHazardValue) },
|
||||
_ => cell.Hazards
|
||||
};
|
||||
|
||||
return cell with { Hazards = hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
using ReactorMaintenance.Simulation.Effects;
|
||||
using ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEnumerable<IAreaSimulationEffect> areaEffects, IEnumerable<Hazard> hazards)
|
||||
{
|
||||
@@ -23,14 +26,14 @@ public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEn
|
||||
var seen = new HashSet<ForecastKey>();
|
||||
var forecastLevel = level with { Cells = level.Cells.ToArray(), Forecasts = Array.Empty<Forecast>() };
|
||||
if (forecastLevel.Global.Lost)
|
||||
AddHazardForecasts(forecasts, seen, forecastLevel, Balancing.CurrentForecastTurn);
|
||||
AddHazardForecasts(forecasts, seen, forecastLevel, Balancing.Current.CurrentForecastTurn);
|
||||
|
||||
AddReactorReadyForecast(forecasts, seen, forecastLevel, Balancing.CurrentForecastTurn);
|
||||
AddReactorReadyForecast(forecasts, seen, forecastLevel, Balancing.Current.CurrentForecastTurn);
|
||||
|
||||
if (IsReactorReady(forecastLevel) || forecastLevel.Global.Lost || forecastLevel.Global.ReactorActivated)
|
||||
return forecasts.OrderBy(f => f.Turns).ThenBy(f => f.Message).ToArray();
|
||||
|
||||
for (var step = Balancing.TurnIncrement; step <= Balancing.MaxForecastStepCount; step++)
|
||||
for (var step = Balancing.Current.TurnIncrement; step <= Balancing.Current.MaxForecastStepCount; step++)
|
||||
{
|
||||
forecastLevel = AdvanceTurn(forecastLevel, false);
|
||||
AddHazardForecasts(forecasts, seen, forecastLevel, step);
|
||||
@@ -60,9 +63,9 @@ public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEn
|
||||
{
|
||||
var cells = level.Cells.ToArray();
|
||||
|
||||
for (var y = Balancing.FirstGridCoordinate; y < level.Height; y++)
|
||||
for (var y = Balancing.Current.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
for (var x = Balancing.FirstGridCoordinate; x < level.Width; x++)
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < level.Width; x++)
|
||||
{
|
||||
var position = new GridPosition(x, y);
|
||||
var index = level.Index(position);
|
||||
@@ -84,7 +87,7 @@ public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEn
|
||||
var global = UpdateGlobal(level, cells);
|
||||
var next = level with {
|
||||
Cells = cells,
|
||||
Global = global with { Turn = level.Global.Turn + Balancing.TurnIncrement }
|
||||
Global = global with { Turn = level.Global.Turn + Balancing.Current.TurnIncrement }
|
||||
};
|
||||
|
||||
return updateForecasts ? next with { Forecasts = Forecast(next) } : next;
|
||||
@@ -116,14 +119,14 @@ public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEn
|
||||
var reactorHeat = cells.Where(c => c.Kind == ECellKind.Reactor).Select(c => c.Hazards.Heat).DefaultIfEmpty(level.Global.CoreHeat).Max();
|
||||
var poweredGenerators = cells.Count(c => c is { Kind: ECellKind.Generator, Powered: true, Hazards.Fire: false });
|
||||
var poweredPumps = cells.Count(c => c is { Kind: ECellKind.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var damagedCriticalCells = cells.Count(c => c.Kind is ECellKind.Reactor or ECellKind.Generator or ECellKind.CoolingPump && c.Hazards.Stability <= Balancing.CriticalCellStabilityThreshold);
|
||||
var damagedCriticalCells = cells.Count(c => c.Kind is ECellKind.Reactor or ECellKind.Generator or ECellKind.CoolingPump && c.Hazards.Stability <= Balancing.Current.CriticalCellStabilityThreshold);
|
||||
var stability = Rules.Clamp(level.Global.FacilityStability - damagedCriticalCells);
|
||||
var lost = reactorHeat >= Balancing.MeltdownCoreHeatThreshold || stability <= Balancing.StabilityCollapseThreshold;
|
||||
var status = lost ? reactorHeat >= Balancing.MeltdownCoreHeatThreshold ? "CORE MELTDOWN" : "FACILITY STABILITY COLLAPSE" : "STABILIZE SYSTEMS";
|
||||
var lost = reactorHeat >= Balancing.Current.MeltdownCoreHeatThreshold || stability <= Balancing.Current.StabilityCollapseThreshold;
|
||||
var status = lost ? reactorHeat >= Balancing.Current.MeltdownCoreHeatThreshold ? "CORE MELTDOWN" : "FACILITY STABILITY COLLAPSE" : "STABILIZE SYSTEMS";
|
||||
var global = level.Global with {
|
||||
CoreHeat = Rules.Clamp(reactorHeat - poweredPumps),
|
||||
Power = Rules.Clamp(poweredGenerators * Balancing.GeneratorPowerOutput),
|
||||
Cooling = Rules.Clamp(poweredPumps * Balancing.CoolingPumpOutput),
|
||||
Power = Rules.Clamp(poweredGenerators * Balancing.Current.GeneratorPowerOutput),
|
||||
Cooling = Rules.Clamp(poweredPumps * Balancing.Current.CoolingPumpOutput),
|
||||
FacilityStability = stability,
|
||||
Lost = lost,
|
||||
Status = status
|
||||
@@ -135,9 +138,9 @@ public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEn
|
||||
private static bool IsReactorReady(LevelState level)
|
||||
{
|
||||
var hasReactor = level.Cells.Any(c => c.Kind == ECellKind.Reactor);
|
||||
var hasStablePower = level.Global.Power >= Balancing.ReactorReadyPowerThreshold || level.Cells.Any(c => c is { Kind: ECellKind.Generator, Powered: true, Hazards.Fire: false });
|
||||
var hasCooling = level.Global.Cooling >= Balancing.ReactorReadyCoolingThreshold || level.Cells.Any(c => c is { Kind: ECellKind.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var reactorStable = level.Global.CoreHeat < Balancing.ReactorReadyCoreHeatThreshold;
|
||||
var hasStablePower = level.Global.Power >= Balancing.Current.ReactorReadyPowerThreshold || level.Cells.Any(c => c is { Kind: ECellKind.Generator, Powered: true, Hazards.Fire: false });
|
||||
var hasCooling = level.Global.Cooling >= Balancing.Current.ReactorReadyCoolingThreshold || level.Cells.Any(c => c is { Kind: ECellKind.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var reactorStable = level.Global.CoreHeat < Balancing.Current.ReactorReadyCoreHeatThreshold;
|
||||
return hasReactor && hasStablePower && hasCooling && reactorStable && !level.Global.Lost;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user