Latest
This commit is contained in:
@@ -1,78 +1,78 @@
|
||||
using ReactorMaintenance.Simulation.Difficulties;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public abstract class Balancing
|
||||
{
|
||||
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; }
|
||||
using ReactorMaintenance.Simulation.Difficulties;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public abstract class Balancing
|
||||
{
|
||||
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,76 +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;
|
||||
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;
|
||||
}
|
||||
@@ -1,35 +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
|
||||
};
|
||||
}
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,30 +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 { Prop: ECellProp.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() };
|
||||
}
|
||||
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 { Prop: ECellProp.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,8 +1,8 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public interface IAreaSimulationEffect
|
||||
{
|
||||
CellState[] Apply(LevelState level, CellState[] cells);
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public interface IAreaSimulationEffect
|
||||
{
|
||||
CellState[] Apply(LevelState level, CellState[] cells);
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public interface ISimulationEffect
|
||||
{
|
||||
CellState Apply(CellState cell);
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public interface ISimulationEffect
|
||||
{
|
||||
CellState Apply(CellState cell);
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class MachineEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var hazards = cell.Prop switch {
|
||||
ECellProp.Generator when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.Current.GeneratorHeatIncrease },
|
||||
ECellProp.CoolingPump when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat - Balancing.Current.CoolingPumpHeatReduction },
|
||||
ECellProp.Reactor => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.Current.ReactorHeatIncrease },
|
||||
_ => cell.Hazards
|
||||
};
|
||||
|
||||
return cell with { Hazards = hazards.Clamp() };
|
||||
}
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Effects;
|
||||
|
||||
public sealed class MachineEffect : ISimulationEffect
|
||||
{
|
||||
public CellState Apply(CellState cell)
|
||||
{
|
||||
var hazards = cell.Prop switch {
|
||||
ECellProp.Generator when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.Current.GeneratorHeatIncrease },
|
||||
ECellProp.CoolingPump when cell.Powered => cell.Hazards with { Heat = cell.Hazards.Heat - Balancing.Current.CoolingPumpHeatReduction },
|
||||
ECellProp.Reactor => cell.Hazards with { Heat = cell.Hazards.Heat + Balancing.Current.ReactorHeatIncrease },
|
||||
_ => cell.Hazards
|
||||
};
|
||||
|
||||
return cell with { Hazards = hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
@@ -1,28 +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() };
|
||||
}
|
||||
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,37 +1,37 @@
|
||||
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.Current.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
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.Current.SmokeSpreadThreshold)
|
||||
continue;
|
||||
|
||||
SpreadToNeighbors(level, next, position);
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
private static void SpreadToNeighbors(LevelState level, CellState[] next, GridPosition position)
|
||||
{
|
||||
foreach (var neighbor in position.Neighbors().Where(level.InBounds))
|
||||
{
|
||||
var neighborCell = next[level.Index(neighbor)];
|
||||
if (!neighborCell.IsWalkable || neighborCell.DoorLocked)
|
||||
continue;
|
||||
|
||||
next[level.Index(neighbor)] = neighborCell with { Hazards = neighborCell.Hazards with { Smoke = Rules.Clamp(neighborCell.Hazards.Smoke + Balancing.Current.SmokeSpreadIncrease) } };
|
||||
}
|
||||
}
|
||||
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.Current.FirstGridCoordinate; y < level.Height; y++)
|
||||
{
|
||||
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.Current.SmokeSpreadThreshold)
|
||||
continue;
|
||||
|
||||
SpreadToNeighbors(level, next, position);
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
private static void SpreadToNeighbors(LevelState level, CellState[] next, GridPosition position)
|
||||
{
|
||||
foreach (var neighbor in position.Neighbors().Where(level.InBounds))
|
||||
{
|
||||
var neighborCell = next[level.Index(neighbor)];
|
||||
if (!neighborCell.IsWalkable || neighborCell.DoorLocked)
|
||||
continue;
|
||||
|
||||
next[level.Index(neighbor)] = neighborCell with { Hazards = neighborCell.Hazards with { Smoke = Rules.Clamp(neighborCell.Hazards.Smoke + Balancing.Current.SmokeSpreadIncrease) } };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public abstract class Hazard
|
||||
{
|
||||
public abstract IEnumerable<Forecast> Predict(LevelState level, int turns);
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public abstract class Hazard
|
||||
{
|
||||
public abstract IEnumerable<Forecast> Predict(LevelState level, int turns);
|
||||
}
|
||||
@@ -1,20 +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");
|
||||
}
|
||||
}
|
||||
}
|
||||
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,12 +1,12 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class MeltdownHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
if (level.Global is { Lost: true, Status: "CORE MELTDOWN" })
|
||||
yield return new(EFailureKind.Meltdown, null, turns, "CORE MELTDOWN APPROACHING");
|
||||
}
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class MeltdownHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
if (level.Global is { Lost: true, Status: "CORE MELTDOWN" })
|
||||
yield return new(EFailureKind.Meltdown, null, turns, "CORE MELTDOWN APPROACHING");
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
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.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 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
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.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 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,12 +1,12 @@
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class StabilityCollapseHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
if (level.Global is { Lost: true, Status: "FACILITY STABILITY COLLAPSE" })
|
||||
yield return new(EFailureKind.StabilityCollapse, null, turns, "FACILITY STABILITY COLLAPSE APPROACHING");
|
||||
}
|
||||
using ReactorMaintenance.Simulation;
|
||||
|
||||
namespace ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
public sealed class StabilityCollapseHazard : Hazard
|
||||
{
|
||||
public override IEnumerable<Forecast> Predict(LevelState level, int turns)
|
||||
{
|
||||
if (level.Global is { Lost: true, Status: "FACILITY STABILITY COLLAPSE" })
|
||||
yield return new(EFailureKind.StabilityCollapse, null, turns, "FACILITY STABILITY COLLAPSE APPROACHING");
|
||||
}
|
||||
}
|
||||
@@ -1,119 +1,119 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EEditorTool
|
||||
{
|
||||
Cursor,
|
||||
Floor,
|
||||
Wall,
|
||||
Reactor,
|
||||
CoolingPump,
|
||||
Generator,
|
||||
PressureRegulator,
|
||||
DiagnosticTerminal,
|
||||
ControlTerminal,
|
||||
CoolantPipe,
|
||||
FuelPipe,
|
||||
PressurePipe,
|
||||
Leak,
|
||||
Repair,
|
||||
Heat,
|
||||
Fire,
|
||||
Robot
|
||||
}
|
||||
|
||||
public static class LevelEditor
|
||||
{
|
||||
public static LevelState Apply(LevelState level, GridPosition position, EEditorTool tool)
|
||||
{
|
||||
if (!level.InBounds(position))
|
||||
return level;
|
||||
|
||||
if (tool == EEditorTool.Robot)
|
||||
return level.GetCell(position).IsWalkable ? level with { Robot = position } : level;
|
||||
|
||||
var cell = level.GetCell(position);
|
||||
cell = tool switch {
|
||||
EEditorTool.Cursor => cell,
|
||||
EEditorTool.Floor => cell with { Terrain = ECellTerrain.Floor },
|
||||
EEditorTool.Wall => cell with {
|
||||
Terrain = ECellTerrain.Wall,
|
||||
Prop = ECellProp.None,
|
||||
Pipe = EPipeMedium.None,
|
||||
Flow = Balancing.Current.MinHazardValue,
|
||||
Pressure = Balancing.Current.MinHazardValue,
|
||||
LeakRate = Balancing.Current.MinHazardValue,
|
||||
PipeOpen = false,
|
||||
Powered = false
|
||||
},
|
||||
EEditorTool.Reactor => cell with { Terrain = ECellTerrain.Floor, Prop = ECellProp.Reactor },
|
||||
EEditorTool.CoolingPump => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.CoolingPump,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.Generator => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.Generator,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.PressureRegulator => cell with { Terrain = ECellTerrain.Floor, Prop = ECellProp.PressureRegulator },
|
||||
EEditorTool.DiagnosticTerminal => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.DiagnosticTerminal,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.ControlTerminal => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.ControlTerminal,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.CoolantPipe => cell with {
|
||||
Pipe = EPipeMedium.Coolant,
|
||||
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.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.Current.DefaultPressurePipeFlow,
|
||||
Pressure = Balancing.Current.DefaultPressurePipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.Current.DefaultEditedPipeIntegrity),
|
||||
PipeOpen = true
|
||||
},
|
||||
EEditorTool.Leak => cell with {
|
||||
LeakRate = Math.Max(Balancing.Current.MinimumLeakRate, cell.LeakRate),
|
||||
Integrity = Math.Min(cell.Integrity, Balancing.Current.DamagedPipeIntegrity)
|
||||
},
|
||||
EEditorTool.Repair => cell with {
|
||||
LeakRate = Balancing.Current.RepairedLeakRate,
|
||||
Integrity = Balancing.Current.DefaultCellIntegrity,
|
||||
Hazards = cell.Hazards with {
|
||||
Fire = false,
|
||||
ElectricalCharge = Balancing.Current.RepairedElectricalCharge
|
||||
}
|
||||
},
|
||||
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.Current.FireToolMinimumHeat),
|
||||
Smoke = Math.Max(cell.Hazards.Smoke, Balancing.Current.FireToolMinimumSmoke)
|
||||
}
|
||||
},
|
||||
_ => cell
|
||||
};
|
||||
|
||||
if (cell.Terrain == ECellTerrain.Wall)
|
||||
cell = cell with { Hazards = new() };
|
||||
|
||||
return level.SetCell(position, cell);
|
||||
}
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum EEditorTool
|
||||
{
|
||||
Cursor,
|
||||
Floor,
|
||||
Wall,
|
||||
Reactor,
|
||||
CoolingPump,
|
||||
Generator,
|
||||
PressureRegulator,
|
||||
DiagnosticTerminal,
|
||||
ControlTerminal,
|
||||
CoolantPipe,
|
||||
FuelPipe,
|
||||
PressurePipe,
|
||||
Leak,
|
||||
Repair,
|
||||
Heat,
|
||||
Fire,
|
||||
Robot
|
||||
}
|
||||
|
||||
public static class LevelEditor
|
||||
{
|
||||
public static LevelState Apply(LevelState level, GridPosition position, EEditorTool tool)
|
||||
{
|
||||
if (!level.InBounds(position))
|
||||
return level;
|
||||
|
||||
if (tool == EEditorTool.Robot)
|
||||
return level.GetCell(position).IsWalkable ? level with { Robot = position } : level;
|
||||
|
||||
var cell = level.GetCell(position);
|
||||
cell = tool switch {
|
||||
EEditorTool.Cursor => cell,
|
||||
EEditorTool.Floor => cell with { Terrain = ECellTerrain.Floor },
|
||||
EEditorTool.Wall => cell with {
|
||||
Terrain = ECellTerrain.Wall,
|
||||
Prop = ECellProp.None,
|
||||
Pipe = EPipeMedium.None,
|
||||
Flow = Balancing.Current.MinHazardValue,
|
||||
Pressure = Balancing.Current.MinHazardValue,
|
||||
LeakRate = Balancing.Current.MinHazardValue,
|
||||
PipeOpen = false,
|
||||
Powered = false
|
||||
},
|
||||
EEditorTool.Reactor => cell with { Terrain = ECellTerrain.Floor, Prop = ECellProp.Reactor },
|
||||
EEditorTool.CoolingPump => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.CoolingPump,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.Generator => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.Generator,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.PressureRegulator => cell with { Terrain = ECellTerrain.Floor, Prop = ECellProp.PressureRegulator },
|
||||
EEditorTool.DiagnosticTerminal => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.DiagnosticTerminal,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.ControlTerminal => cell with {
|
||||
Terrain = ECellTerrain.Floor,
|
||||
Prop = ECellProp.ControlTerminal,
|
||||
Powered = true
|
||||
},
|
||||
EEditorTool.CoolantPipe => cell with {
|
||||
Pipe = EPipeMedium.Coolant,
|
||||
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.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.Current.DefaultPressurePipeFlow,
|
||||
Pressure = Balancing.Current.DefaultPressurePipePressure,
|
||||
Integrity = Math.Max(cell.Integrity, Balancing.Current.DefaultEditedPipeIntegrity),
|
||||
PipeOpen = true
|
||||
},
|
||||
EEditorTool.Leak => cell with {
|
||||
LeakRate = Math.Max(Balancing.Current.MinimumLeakRate, cell.LeakRate),
|
||||
Integrity = Math.Min(cell.Integrity, Balancing.Current.DamagedPipeIntegrity)
|
||||
},
|
||||
EEditorTool.Repair => cell with {
|
||||
LeakRate = Balancing.Current.RepairedLeakRate,
|
||||
Integrity = Balancing.Current.DefaultCellIntegrity,
|
||||
Hazards = cell.Hazards with {
|
||||
Fire = false,
|
||||
ElectricalCharge = Balancing.Current.RepairedElectricalCharge
|
||||
}
|
||||
},
|
||||
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.Current.FireToolMinimumHeat),
|
||||
Smoke = Math.Max(cell.Hazards.Smoke, Balancing.Current.FireToolMinimumSmoke)
|
||||
}
|
||||
},
|
||||
_ => cell
|
||||
};
|
||||
|
||||
if (cell.Terrain == ECellTerrain.Wall)
|
||||
cell = cell with { Hazards = new() };
|
||||
|
||||
return level.SetCell(position, cell);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,39 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class LevelSerializer
|
||||
{
|
||||
private const int c_CurrentVersion = 1;
|
||||
|
||||
public static string Serialize(LevelState level)
|
||||
{
|
||||
return JsonSerializer.Serialize(new LevelFile {
|
||||
Version = c_CurrentVersion,
|
||||
Level = level
|
||||
}, Options);
|
||||
}
|
||||
|
||||
public static LevelState Deserialize(string json)
|
||||
{
|
||||
var file = JsonSerializer.Deserialize<LevelFile>(json, Options) ?? throw new InvalidOperationException("Level file did not contain a level.");
|
||||
var level = file.Version switch {
|
||||
c_CurrentVersion => file.Level,
|
||||
_ => throw new InvalidOperationException($"Unsupported level file version {file.Version}.")
|
||||
};
|
||||
|
||||
return level.Cells.Length != level.Width * level.Height ? throw new InvalidOperationException("Level cell count does not match its dimensions.") : level;
|
||||
}
|
||||
|
||||
private static readonly JsonSerializerOptions Options = new() {
|
||||
WriteIndented = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
};
|
||||
|
||||
private sealed record LevelFile
|
||||
{
|
||||
public int Version { get; init; }
|
||||
public LevelState Level { get; init; } = new();
|
||||
}
|
||||
}
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public static class LevelSerializer
|
||||
{
|
||||
private const int c_CurrentVersion = 1;
|
||||
|
||||
public static string Serialize(LevelState level)
|
||||
{
|
||||
return JsonSerializer.Serialize(new LevelFile {
|
||||
Version = c_CurrentVersion,
|
||||
Level = level
|
||||
}, Options);
|
||||
}
|
||||
|
||||
public static LevelState Deserialize(string json)
|
||||
{
|
||||
var file = JsonSerializer.Deserialize<LevelFile>(json, Options) ?? throw new InvalidOperationException("Level file did not contain a level.");
|
||||
var level = file.Version switch {
|
||||
c_CurrentVersion => file.Level,
|
||||
_ => throw new InvalidOperationException($"Unsupported level file version {file.Version}.")
|
||||
};
|
||||
|
||||
return level.Cells.Length != level.Width * level.Height ? throw new InvalidOperationException("Level cell count does not match its dimensions.") : level;
|
||||
}
|
||||
|
||||
private static readonly JsonSerializerOptions Options = new() {
|
||||
WriteIndented = true,
|
||||
Converters = { new JsonStringEnumConverter() }
|
||||
};
|
||||
|
||||
private sealed record LevelFile
|
||||
{
|
||||
public int Version { get; init; }
|
||||
public LevelState Level { get; init; } = new();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,181 +1,181 @@
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ECellTerrain
|
||||
{
|
||||
Floor,
|
||||
Wall
|
||||
}
|
||||
|
||||
public enum ECellProp
|
||||
{
|
||||
None,
|
||||
Reactor,
|
||||
CoolingPump,
|
||||
Generator,
|
||||
PressureRegulator,
|
||||
DiagnosticTerminal,
|
||||
ControlTerminal
|
||||
}
|
||||
|
||||
public enum EPipeMedium
|
||||
{
|
||||
None,
|
||||
Pressure,
|
||||
Coolant,
|
||||
Fuel
|
||||
}
|
||||
|
||||
public enum EFailureKind
|
||||
{
|
||||
PipeBurst,
|
||||
Ignition,
|
||||
Meltdown,
|
||||
StabilityCollapse,
|
||||
ReactorReady
|
||||
}
|
||||
|
||||
public sealed record GridPosition(int X, int Y)
|
||||
{
|
||||
public IEnumerable<GridPosition> Neighbors()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record HazardState
|
||||
{
|
||||
public HazardState Clamp()
|
||||
{
|
||||
return this with {
|
||||
Heat = Rules.Clamp(Heat),
|
||||
Smoke = Rules.Clamp(Smoke),
|
||||
FuelVapor = Rules.Clamp(FuelVapor),
|
||||
LiquidFuel = Rules.Clamp(LiquidFuel),
|
||||
CoolantPooling = Rules.Clamp(CoolantPooling),
|
||||
ElectricalCharge = Rules.Clamp(ElectricalCharge),
|
||||
Stability = Rules.Clamp(Stability)
|
||||
};
|
||||
}
|
||||
|
||||
public int Heat { get; init; }
|
||||
public int Smoke { get; init; }
|
||||
public int FuelVapor { get; init; }
|
||||
public int LiquidFuel { get; init; }
|
||||
public int CoolantPooling { get; init; }
|
||||
public int ElectricalCharge { get; init; }
|
||||
public int Stability { get; init; } = Balancing.Current.DefaultHazardStability;
|
||||
public bool Fire { get; init; }
|
||||
}
|
||||
|
||||
public sealed record CellState
|
||||
{
|
||||
public ECellTerrain Terrain { get; init; } = ECellTerrain.Floor;
|
||||
public ECellProp Prop { get; init; }
|
||||
public EPipeMedium Pipe { get; init; }
|
||||
public int Flow { get; init; }
|
||||
public int Pressure { get; init; }
|
||||
public int Integrity { get; init; } = Balancing.Current.DefaultCellIntegrity;
|
||||
public int LeakRate { get; init; }
|
||||
public bool PipeOpen { get; init; } = true;
|
||||
public bool Powered { get; init; }
|
||||
public bool DoorLocked { get; init; }
|
||||
public HazardState Hazards { get; init; } = new();
|
||||
public bool IsWalkable => Terrain != ECellTerrain.Wall;
|
||||
public bool HasPipe => Pipe != EPipeMedium.None;
|
||||
}
|
||||
|
||||
public sealed record GlobalState
|
||||
{
|
||||
public int Turn { get; init; }
|
||||
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";
|
||||
}
|
||||
|
||||
public sealed record Forecast(EFailureKind Kind, GridPosition? Position, int Turns, string Message);
|
||||
|
||||
public sealed record LevelState
|
||||
{
|
||||
public static LevelState Create(string name, int width, int height)
|
||||
{
|
||||
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.Current.FirstGridCoordinate; y < height; y++)
|
||||
{
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < width; x++)
|
||||
{
|
||||
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 { Terrain = ECellTerrain.Wall };
|
||||
}
|
||||
}
|
||||
|
||||
return new() {
|
||||
Name = name,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Cells = cells,
|
||||
Robot = new(Balancing.Current.DefaultRobotCoordinate, Balancing.Current.DefaultRobotCoordinate)
|
||||
};
|
||||
}
|
||||
|
||||
public CellState GetCell(GridPosition position)
|
||||
{
|
||||
EnsureInBounds(position);
|
||||
return Cells[Index(position)];
|
||||
}
|
||||
|
||||
public LevelState SetCell(GridPosition position, CellState cell)
|
||||
{
|
||||
EnsureInBounds(position);
|
||||
var cells = Cells.ToArray();
|
||||
cells[Index(position)] = cell;
|
||||
return this with { Cells = cells };
|
||||
}
|
||||
|
||||
public bool InBounds(GridPosition position)
|
||||
{
|
||||
return position.X >= Balancing.Current.FirstGridCoordinate && position.Y >= Balancing.Current.FirstGridCoordinate && position.X < Width && position.Y < Height;
|
||||
}
|
||||
|
||||
public int Index(GridPosition position)
|
||||
{
|
||||
return position.Y * Width + position.X;
|
||||
}
|
||||
|
||||
private void EnsureInBounds(GridPosition position)
|
||||
{
|
||||
if (!InBounds(position))
|
||||
throw new ArgumentOutOfRangeException(nameof(position), $"Position {position.X},{position.Y} is outside {Width}x{Height}.");
|
||||
}
|
||||
|
||||
private static CellState[] CreateCells(int width, int height)
|
||||
{
|
||||
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.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>();
|
||||
}
|
||||
|
||||
internal static class Rules
|
||||
{
|
||||
public static int Clamp(int value)
|
||||
{
|
||||
return Math.Clamp(value, Balancing.Current.MinHazardValue, Balancing.Current.MaxHazardValue);
|
||||
}
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public enum ECellTerrain
|
||||
{
|
||||
Floor,
|
||||
Wall
|
||||
}
|
||||
|
||||
public enum ECellProp
|
||||
{
|
||||
None,
|
||||
Reactor,
|
||||
CoolingPump,
|
||||
Generator,
|
||||
PressureRegulator,
|
||||
DiagnosticTerminal,
|
||||
ControlTerminal
|
||||
}
|
||||
|
||||
public enum EPipeMedium
|
||||
{
|
||||
None,
|
||||
Pressure,
|
||||
Coolant,
|
||||
Fuel
|
||||
}
|
||||
|
||||
public enum EFailureKind
|
||||
{
|
||||
PipeBurst,
|
||||
Ignition,
|
||||
Meltdown,
|
||||
StabilityCollapse,
|
||||
ReactorReady
|
||||
}
|
||||
|
||||
public sealed record GridPosition(int X, int Y)
|
||||
{
|
||||
public IEnumerable<GridPosition> Neighbors()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record HazardState
|
||||
{
|
||||
public HazardState Clamp()
|
||||
{
|
||||
return this with {
|
||||
Heat = Rules.Clamp(Heat),
|
||||
Smoke = Rules.Clamp(Smoke),
|
||||
FuelVapor = Rules.Clamp(FuelVapor),
|
||||
LiquidFuel = Rules.Clamp(LiquidFuel),
|
||||
CoolantPooling = Rules.Clamp(CoolantPooling),
|
||||
ElectricalCharge = Rules.Clamp(ElectricalCharge),
|
||||
Stability = Rules.Clamp(Stability)
|
||||
};
|
||||
}
|
||||
|
||||
public int Heat { get; init; }
|
||||
public int Smoke { get; init; }
|
||||
public int FuelVapor { get; init; }
|
||||
public int LiquidFuel { get; init; }
|
||||
public int CoolantPooling { get; init; }
|
||||
public int ElectricalCharge { get; init; }
|
||||
public int Stability { get; init; } = Balancing.Current.DefaultHazardStability;
|
||||
public bool Fire { get; init; }
|
||||
}
|
||||
|
||||
public sealed record CellState
|
||||
{
|
||||
public ECellTerrain Terrain { get; init; } = ECellTerrain.Floor;
|
||||
public ECellProp Prop { get; init; }
|
||||
public EPipeMedium Pipe { get; init; }
|
||||
public int Flow { get; init; }
|
||||
public int Pressure { get; init; }
|
||||
public int Integrity { get; init; } = Balancing.Current.DefaultCellIntegrity;
|
||||
public int LeakRate { get; init; }
|
||||
public bool PipeOpen { get; init; } = true;
|
||||
public bool Powered { get; init; }
|
||||
public bool DoorLocked { get; init; }
|
||||
public HazardState Hazards { get; init; } = new();
|
||||
public bool IsWalkable => Terrain != ECellTerrain.Wall;
|
||||
public bool HasPipe => Pipe != EPipeMedium.None;
|
||||
}
|
||||
|
||||
public sealed record GlobalState
|
||||
{
|
||||
public int Turn { get; init; }
|
||||
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";
|
||||
}
|
||||
|
||||
public sealed record Forecast(EFailureKind Kind, GridPosition? Position, int Turns, string Message);
|
||||
|
||||
public sealed record LevelState
|
||||
{
|
||||
public static LevelState Create(string name, int width, int height)
|
||||
{
|
||||
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.Current.FirstGridCoordinate; y < height; y++)
|
||||
{
|
||||
for (var x = Balancing.Current.FirstGridCoordinate; x < width; x++)
|
||||
{
|
||||
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 { Terrain = ECellTerrain.Wall };
|
||||
}
|
||||
}
|
||||
|
||||
return new() {
|
||||
Name = name,
|
||||
Width = width,
|
||||
Height = height,
|
||||
Cells = cells,
|
||||
Robot = new(Balancing.Current.DefaultRobotCoordinate, Balancing.Current.DefaultRobotCoordinate)
|
||||
};
|
||||
}
|
||||
|
||||
public CellState GetCell(GridPosition position)
|
||||
{
|
||||
EnsureInBounds(position);
|
||||
return Cells[Index(position)];
|
||||
}
|
||||
|
||||
public LevelState SetCell(GridPosition position, CellState cell)
|
||||
{
|
||||
EnsureInBounds(position);
|
||||
var cells = Cells.ToArray();
|
||||
cells[Index(position)] = cell;
|
||||
return this with { Cells = cells };
|
||||
}
|
||||
|
||||
public bool InBounds(GridPosition position)
|
||||
{
|
||||
return position.X >= Balancing.Current.FirstGridCoordinate && position.Y >= Balancing.Current.FirstGridCoordinate && position.X < Width && position.Y < Height;
|
||||
}
|
||||
|
||||
public int Index(GridPosition position)
|
||||
{
|
||||
return position.Y * Width + position.X;
|
||||
}
|
||||
|
||||
private void EnsureInBounds(GridPosition position)
|
||||
{
|
||||
if (!InBounds(position))
|
||||
throw new ArgumentOutOfRangeException(nameof(position), $"Position {position.X},{position.Y} is outside {Width}x{Height}.");
|
||||
}
|
||||
|
||||
private static CellState[] CreateCells(int width, int height)
|
||||
{
|
||||
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.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>();
|
||||
}
|
||||
|
||||
internal static class Rules
|
||||
{
|
||||
public static int Clamp(int value)
|
||||
{
|
||||
return Math.Clamp(value, Balancing.Current.MinHazardValue, Balancing.Current.MaxHazardValue);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,150 +1,150 @@
|
||||
using ReactorMaintenance.Simulation.Effects;
|
||||
using ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEnumerable<IAreaSimulationEffect> areaEffects, IEnumerable<Hazard> hazards)
|
||||
{
|
||||
private sealed record ForecastKey(EFailureKind Kind, GridPosition? Position);
|
||||
|
||||
public SimulationEngine()
|
||||
: this(
|
||||
[new PipeLeakEffect(), new MachineEffect(), new FireAndElectricalHazardEffect(), new CellIntegrityEffect()],
|
||||
[new SmokeSpreadEffect()],
|
||||
[new PipeBurstHazard(), new IgnitionHazard(), new MeltdownHazard(), new StabilityCollapseHazard()])
|
||||
{
|
||||
}
|
||||
|
||||
public LevelState AdvanceTurn(LevelState level)
|
||||
{
|
||||
return AdvanceTurn(level, true);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Forecast> Forecast(LevelState level)
|
||||
{
|
||||
var forecasts = new List<Forecast>();
|
||||
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.Current.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.Current.TurnIncrement; step <= Balancing.Current.MaxForecastStepCount; step++)
|
||||
{
|
||||
forecastLevel = AdvanceTurn(forecastLevel, false);
|
||||
AddHazardForecasts(forecasts, seen, forecastLevel, step);
|
||||
AddReactorReadyForecast(forecasts, seen, forecastLevel, step);
|
||||
|
||||
if (forecastLevel.Global.Lost || IsReactorReady(forecastLevel) || forecastLevel.Global.ReactorActivated)
|
||||
break;
|
||||
}
|
||||
|
||||
return forecasts.OrderBy(f => f.Turns).ThenBy(f => f.Message).ToArray();
|
||||
}
|
||||
|
||||
public LevelState ActivateReactor(LevelState level)
|
||||
{
|
||||
if (!IsReactorReady(level))
|
||||
return level with { Global = level.Global with { Status = "REACTOR NOT READY" } };
|
||||
|
||||
return level with {
|
||||
Global = level.Global with {
|
||||
ReactorActivated = true,
|
||||
Status = "REACTOR ONLINE"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private LevelState AdvanceTurn(LevelState level, bool updateForecasts)
|
||||
{
|
||||
var cells = level.Cells.ToArray();
|
||||
|
||||
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 index = level.Index(position);
|
||||
var cell = cells[index];
|
||||
|
||||
if (!cell.IsWalkable)
|
||||
continue;
|
||||
|
||||
foreach (var effect in m_Effects)
|
||||
cell = effect.Apply(cell);
|
||||
|
||||
cells[index] = cell with { Hazards = cell.Hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var areaEffect in m_AreaEffects)
|
||||
cells = areaEffect.Apply(level, cells);
|
||||
|
||||
var global = UpdateGlobal(level, cells);
|
||||
var next = level with {
|
||||
Cells = cells,
|
||||
Global = global with { Turn = level.Global.Turn + Balancing.Current.TurnIncrement }
|
||||
};
|
||||
|
||||
return updateForecasts ? next with { Forecasts = Forecast(next) } : next;
|
||||
}
|
||||
|
||||
private void AddHazardForecasts(List<Forecast> forecasts, HashSet<ForecastKey> seen, LevelState level, int turns)
|
||||
{
|
||||
foreach (var hazard in m_Hazards)
|
||||
{
|
||||
foreach (var forecast in hazard.Predict(level, turns))
|
||||
AddForecast(forecasts, seen, forecast);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddReactorReadyForecast(List<Forecast> forecasts, HashSet<ForecastKey> seen, LevelState level, int turns)
|
||||
{
|
||||
if (IsReactorReady(level))
|
||||
AddForecast(forecasts, seen, new(EFailureKind.ReactorReady, null, turns, "REACTOR READY"));
|
||||
}
|
||||
|
||||
private static void AddForecast(List<Forecast> forecasts, HashSet<ForecastKey> seen, Forecast forecast)
|
||||
{
|
||||
if (seen.Add(new(forecast.Kind, forecast.Position)))
|
||||
forecasts.Add(forecast);
|
||||
}
|
||||
|
||||
private static GlobalState UpdateGlobal(LevelState level, CellState[] cells)
|
||||
{
|
||||
var reactorHeat = cells.Where(c => c.Prop == ECellProp.Reactor).Select(c => c.Hazards.Heat).DefaultIfEmpty(level.Global.CoreHeat).Max();
|
||||
var poweredGenerators = cells.Count(c => c is { Prop: ECellProp.Generator, Powered: true, Hazards.Fire: false });
|
||||
var poweredPumps = cells.Count(c => c is { Prop: ECellProp.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var damagedCriticalCells = cells.Count(c => c.Prop is ECellProp.Reactor or ECellProp.Generator or ECellProp.CoolingPump && c.Hazards.Stability <= Balancing.Current.CriticalCellStabilityThreshold);
|
||||
var stability = Rules.Clamp(level.Global.FacilityStability - damagedCriticalCells);
|
||||
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.Current.GeneratorPowerOutput),
|
||||
Cooling = Rules.Clamp(poweredPumps * Balancing.Current.CoolingPumpOutput),
|
||||
FacilityStability = stability,
|
||||
Lost = lost,
|
||||
Status = status
|
||||
};
|
||||
|
||||
return IsReactorReady(level with { Cells = cells, Global = global }) ? global with { Status = "REACTOR READY" } : global;
|
||||
}
|
||||
|
||||
private static bool IsReactorReady(LevelState level)
|
||||
{
|
||||
var hasReactor = level.Cells.Any(c => c.Prop == ECellProp.Reactor);
|
||||
var hasStablePower = level.Global.Power >= Balancing.Current.ReactorReadyPowerThreshold || level.Cells.Any(c => c is { Prop: ECellProp.Generator, Powered: true, Hazards.Fire: false });
|
||||
var hasCooling = level.Global.Cooling >= Balancing.Current.ReactorReadyCoolingThreshold || level.Cells.Any(c => c is { Prop: ECellProp.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var reactorStable = level.Global.CoreHeat < Balancing.Current.ReactorReadyCoreHeatThreshold;
|
||||
return hasReactor && hasStablePower && hasCooling && reactorStable && !level.Global.Lost;
|
||||
}
|
||||
|
||||
private readonly IReadOnlyList<IAreaSimulationEffect> m_AreaEffects = areaEffects.ToArray();
|
||||
private readonly IReadOnlyList<ISimulationEffect> m_Effects = effects.ToArray();
|
||||
private readonly IReadOnlyList<Hazard> m_Hazards = hazards.ToArray();
|
||||
using ReactorMaintenance.Simulation.Effects;
|
||||
using ReactorMaintenance.Simulation.Hazards;
|
||||
|
||||
namespace ReactorMaintenance.Simulation;
|
||||
|
||||
public sealed class SimulationEngine(IEnumerable<ISimulationEffect> effects, IEnumerable<IAreaSimulationEffect> areaEffects, IEnumerable<Hazard> hazards)
|
||||
{
|
||||
private sealed record ForecastKey(EFailureKind Kind, GridPosition? Position);
|
||||
|
||||
public SimulationEngine()
|
||||
: this(
|
||||
[new PipeLeakEffect(), new MachineEffect(), new FireAndElectricalHazardEffect(), new CellIntegrityEffect()],
|
||||
[new SmokeSpreadEffect()],
|
||||
[new PipeBurstHazard(), new IgnitionHazard(), new MeltdownHazard(), new StabilityCollapseHazard()])
|
||||
{
|
||||
}
|
||||
|
||||
public LevelState AdvanceTurn(LevelState level)
|
||||
{
|
||||
return AdvanceTurn(level, true);
|
||||
}
|
||||
|
||||
public IReadOnlyList<Forecast> Forecast(LevelState level)
|
||||
{
|
||||
var forecasts = new List<Forecast>();
|
||||
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.Current.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.Current.TurnIncrement; step <= Balancing.Current.MaxForecastStepCount; step++)
|
||||
{
|
||||
forecastLevel = AdvanceTurn(forecastLevel, false);
|
||||
AddHazardForecasts(forecasts, seen, forecastLevel, step);
|
||||
AddReactorReadyForecast(forecasts, seen, forecastLevel, step);
|
||||
|
||||
if (forecastLevel.Global.Lost || IsReactorReady(forecastLevel) || forecastLevel.Global.ReactorActivated)
|
||||
break;
|
||||
}
|
||||
|
||||
return forecasts.OrderBy(f => f.Turns).ThenBy(f => f.Message).ToArray();
|
||||
}
|
||||
|
||||
public LevelState ActivateReactor(LevelState level)
|
||||
{
|
||||
if (!IsReactorReady(level))
|
||||
return level with { Global = level.Global with { Status = "REACTOR NOT READY" } };
|
||||
|
||||
return level with {
|
||||
Global = level.Global with {
|
||||
ReactorActivated = true,
|
||||
Status = "REACTOR ONLINE"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private LevelState AdvanceTurn(LevelState level, bool updateForecasts)
|
||||
{
|
||||
var cells = level.Cells.ToArray();
|
||||
|
||||
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 index = level.Index(position);
|
||||
var cell = cells[index];
|
||||
|
||||
if (!cell.IsWalkable)
|
||||
continue;
|
||||
|
||||
foreach (var effect in m_Effects)
|
||||
cell = effect.Apply(cell);
|
||||
|
||||
cells[index] = cell with { Hazards = cell.Hazards.Clamp() };
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var areaEffect in m_AreaEffects)
|
||||
cells = areaEffect.Apply(level, cells);
|
||||
|
||||
var global = UpdateGlobal(level, cells);
|
||||
var next = level with {
|
||||
Cells = cells,
|
||||
Global = global with { Turn = level.Global.Turn + Balancing.Current.TurnIncrement }
|
||||
};
|
||||
|
||||
return updateForecasts ? next with { Forecasts = Forecast(next) } : next;
|
||||
}
|
||||
|
||||
private void AddHazardForecasts(List<Forecast> forecasts, HashSet<ForecastKey> seen, LevelState level, int turns)
|
||||
{
|
||||
foreach (var hazard in m_Hazards)
|
||||
{
|
||||
foreach (var forecast in hazard.Predict(level, turns))
|
||||
AddForecast(forecasts, seen, forecast);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddReactorReadyForecast(List<Forecast> forecasts, HashSet<ForecastKey> seen, LevelState level, int turns)
|
||||
{
|
||||
if (IsReactorReady(level))
|
||||
AddForecast(forecasts, seen, new(EFailureKind.ReactorReady, null, turns, "REACTOR READY"));
|
||||
}
|
||||
|
||||
private static void AddForecast(List<Forecast> forecasts, HashSet<ForecastKey> seen, Forecast forecast)
|
||||
{
|
||||
if (seen.Add(new(forecast.Kind, forecast.Position)))
|
||||
forecasts.Add(forecast);
|
||||
}
|
||||
|
||||
private static GlobalState UpdateGlobal(LevelState level, CellState[] cells)
|
||||
{
|
||||
var reactorHeat = cells.Where(c => c.Prop == ECellProp.Reactor).Select(c => c.Hazards.Heat).DefaultIfEmpty(level.Global.CoreHeat).Max();
|
||||
var poweredGenerators = cells.Count(c => c is { Prop: ECellProp.Generator, Powered: true, Hazards.Fire: false });
|
||||
var poweredPumps = cells.Count(c => c is { Prop: ECellProp.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var damagedCriticalCells = cells.Count(c => c.Prop is ECellProp.Reactor or ECellProp.Generator or ECellProp.CoolingPump && c.Hazards.Stability <= Balancing.Current.CriticalCellStabilityThreshold);
|
||||
var stability = Rules.Clamp(level.Global.FacilityStability - damagedCriticalCells);
|
||||
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.Current.GeneratorPowerOutput),
|
||||
Cooling = Rules.Clamp(poweredPumps * Balancing.Current.CoolingPumpOutput),
|
||||
FacilityStability = stability,
|
||||
Lost = lost,
|
||||
Status = status
|
||||
};
|
||||
|
||||
return IsReactorReady(level with { Cells = cells, Global = global }) ? global with { Status = "REACTOR READY" } : global;
|
||||
}
|
||||
|
||||
private static bool IsReactorReady(LevelState level)
|
||||
{
|
||||
var hasReactor = level.Cells.Any(c => c.Prop == ECellProp.Reactor);
|
||||
var hasStablePower = level.Global.Power >= Balancing.Current.ReactorReadyPowerThreshold || level.Cells.Any(c => c is { Prop: ECellProp.Generator, Powered: true, Hazards.Fire: false });
|
||||
var hasCooling = level.Global.Cooling >= Balancing.Current.ReactorReadyCoolingThreshold || level.Cells.Any(c => c is { Prop: ECellProp.CoolingPump, Powered: true, Hazards.Fire: false });
|
||||
var reactorStable = level.Global.CoreHeat < Balancing.Current.ReactorReadyCoreHeatThreshold;
|
||||
return hasReactor && hasStablePower && hasCooling && reactorStable && !level.Global.Lost;
|
||||
}
|
||||
|
||||
private readonly IReadOnlyList<IAreaSimulationEffect> m_AreaEffects = areaEffects.ToArray();
|
||||
private readonly IReadOnlyList<ISimulationEffect> m_Effects = effects.ToArray();
|
||||
private readonly IReadOnlyList<Hazard> m_Hazards = hazards.ToArray();
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<Application
|
||||
x:Class="ReactorMaintenance.Win2D.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<Application
|
||||
x:Class="ReactorMaintenance.Win2D.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
</Application>
|
||||
@@ -1,19 +1,19 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace ReactorMaintenance.Win2D;
|
||||
|
||||
public partial class App
|
||||
{
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
m_Window = new MainWindow();
|
||||
m_Window.Activate();
|
||||
}
|
||||
|
||||
private Window? m_Window;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace ReactorMaintenance.Win2D;
|
||||
|
||||
public partial class App
|
||||
{
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
m_Window = new MainWindow();
|
||||
m_Window.Activate();
|
||||
}
|
||||
|
||||
private Window? m_Window;
|
||||
}
|
||||
@@ -1,115 +1,115 @@
|
||||
<Window
|
||||
x:Class="ReactorMaintenance.Win2D.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||
Title="Reactor Maintenance">
|
||||
<Grid Background="#16191D">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<CommandBar Grid.Row="0" DefaultLabelPosition="Right" Background="#20252A">
|
||||
<AppBarButton Icon="Add" Label="New" Click="New_Click" />
|
||||
<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click" />
|
||||
<AppBarButton Icon="Save" Label="Save" Click="Save_Click" />
|
||||
<AppBarSeparator />
|
||||
<AppBarButton Icon="Play" Label="Simulate" Click="Simulate_Click" />
|
||||
<AppBarButton Icon="Accept" Label="Activate" Click="Activate_Click" />
|
||||
</CommandBar>
|
||||
|
||||
<Grid Grid.Row="1" ColumnSpacing="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="260" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="300" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ScrollViewer Grid.Column="0" Background="#1C2126">
|
||||
<StackPanel Padding="12" Spacing="10">
|
||||
<TextBlock Text="Tools" FontSize="18" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<ItemsControl x:Name="ToolPicker">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsWrapGrid Orientation="Horizontal" MaximumRowsOrColumns="2" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
||||
Checked="ToolToggle_Checked" ToolTipService.ToolTip="{Binding Label}"
|
||||
Padding="5" Margin="0,0,8,8">
|
||||
<Image Width="96" Height="96" Source="{Binding Icon}" Stretch="Uniform" />
|
||||
</ToggleButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock Text="Brush applies to the selected cell." Foreground="#9EA7AE" TextWrapping="Wrap" />
|
||||
<TextBlock Text="Left click paints. Use Robot to set the start position." Foreground="#9EA7AE"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid Grid.Column="1" Background="#101215">
|
||||
<canvas:CanvasControl
|
||||
x:Name="LevelCanvas"
|
||||
ClearColor="#101215"
|
||||
CreateResources="LevelCanvas_CreateResources"
|
||||
Draw="LevelCanvas_Draw"
|
||||
PointerPressed="LevelCanvas_PointerPressed"
|
||||
PointerMoved="LevelCanvas_PointerMoved"
|
||||
PointerReleased="LevelCanvas_PointerReleased"
|
||||
PointerExited="LevelCanvas_PointerExited"
|
||||
PointerWheelChanged="LevelCanvas_PointerWheelChanged" />
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Column="2" Background="#1C2126">
|
||||
<StackPanel Padding="14" Spacing="12">
|
||||
<TextBlock x:Name="LevelNameText" FontSize="20" FontWeight="SemiBold" Foreground="#F4F1E8"
|
||||
TextWrapping="Wrap" />
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Turn" Foreground="#9EA7AE" />
|
||||
<TextBlock x:Name="TurnText" FontSize="22" Foreground="#F4F1E8" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="Status" Foreground="#9EA7AE" />
|
||||
<TextBlock x:Name="StatusText" FontSize="16" Foreground="#F4F1E8" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="Global Systems" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<TextBlock x:Name="GlobalText" Foreground="#CCD4DA" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Text="Selected Cell" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<TextBlock x:Name="CellText" Foreground="#CCD4DA" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Text="Forecasts" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<ItemsControl x:Name="ForecastList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderBrush="#46515A" BorderThickness="1" Padding="8" Margin="0,0,0,8"
|
||||
CornerRadius="3">
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="32" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Width="28" Height="28" Source="{Binding Icon}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Message}" Foreground="#F4F1E8"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
<Window
|
||||
x:Class="ReactorMaintenance.Win2D.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
|
||||
Title="Reactor Maintenance">
|
||||
<Grid Background="#16191D">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<CommandBar Grid.Row="0" DefaultLabelPosition="Right" Background="#20252A">
|
||||
<AppBarButton Icon="Add" Label="New" Click="New_Click" />
|
||||
<AppBarButton Icon="OpenFile" Label="Open" Click="Open_Click" />
|
||||
<AppBarButton Icon="Save" Label="Save" Click="Save_Click" />
|
||||
<AppBarSeparator />
|
||||
<AppBarButton Icon="Play" Label="Simulate" Click="Simulate_Click" />
|
||||
<AppBarButton Icon="Accept" Label="Activate" Click="Activate_Click" />
|
||||
</CommandBar>
|
||||
|
||||
<Grid Grid.Row="1" ColumnSpacing="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="260" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="300" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ScrollViewer Grid.Column="0" Background="#1C2126">
|
||||
<StackPanel Padding="12" Spacing="10">
|
||||
<TextBlock Text="Tools" FontSize="18" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<ItemsControl x:Name="ToolPicker">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsWrapGrid Orientation="Horizontal" MaximumRowsOrColumns="2" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
||||
Checked="ToolToggle_Checked" ToolTipService.ToolTip="{Binding Label}"
|
||||
Padding="5" Margin="0,0,8,8">
|
||||
<Image Width="96" Height="96" Source="{Binding Icon}" Stretch="Uniform" />
|
||||
</ToggleButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock Text="Brush applies to the selected cell." Foreground="#9EA7AE" TextWrapping="Wrap" />
|
||||
<TextBlock Text="Left click paints. Use Robot to set the start position." Foreground="#9EA7AE"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid Grid.Column="1" Background="#101215">
|
||||
<canvas:CanvasControl
|
||||
x:Name="LevelCanvas"
|
||||
ClearColor="#101215"
|
||||
CreateResources="LevelCanvas_CreateResources"
|
||||
Draw="LevelCanvas_Draw"
|
||||
PointerPressed="LevelCanvas_PointerPressed"
|
||||
PointerMoved="LevelCanvas_PointerMoved"
|
||||
PointerReleased="LevelCanvas_PointerReleased"
|
||||
PointerExited="LevelCanvas_PointerExited"
|
||||
PointerWheelChanged="LevelCanvas_PointerWheelChanged" />
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Column="2" Background="#1C2126">
|
||||
<StackPanel Padding="14" Spacing="12">
|
||||
<TextBlock x:Name="LevelNameText" FontSize="20" FontWeight="SemiBold" Foreground="#F4F1E8"
|
||||
TextWrapping="Wrap" />
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Turn" Foreground="#9EA7AE" />
|
||||
<TextBlock x:Name="TurnText" FontSize="22" Foreground="#F4F1E8" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1">
|
||||
<TextBlock Text="Status" Foreground="#9EA7AE" />
|
||||
<TextBlock x:Name="StatusText" FontSize="16" Foreground="#F4F1E8" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<TextBlock Text="Global Systems" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<TextBlock x:Name="GlobalText" Foreground="#CCD4DA" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Text="Selected Cell" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<TextBlock x:Name="CellText" Foreground="#CCD4DA" TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock Text="Forecasts" FontSize="16" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||
<ItemsControl x:Name="ForecastList">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border BorderBrush="#46515A" BorderThickness="1" Padding="8" Margin="0,0,0,8"
|
||||
CornerRadius="3">
|
||||
<Grid ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="32" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image Width="28" Height="28" Source="{Binding Icon}" />
|
||||
<TextBlock Grid.Column="1" Text="{Binding Message}" Foreground="#F4F1E8"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>ReactorMaintenance.Win2D</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Platforms>x86;x64;arm64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1839" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ReactorMaintenance.Simulation\ReactorMaintenance.Simulation.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Images\**\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>ReactorMaintenance.Win2D</RootNamespace>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<Platforms>x86;x64;arm64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.28000.1839" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260317003" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ReactorMaintenance.Simulation\ReactorMaintenance.Simulation.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Images\**\*.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="ReactorMaintenance.Win2D.app"/>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="ReactorMaintenance.Win2D.app"/>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
|
||||
Reference in New Issue
Block a user