Rename coolant to water

This commit is contained in:
2026-05-14 10:00:08 +02:00
parent 4ac8a77e8e
commit c149fe02a3
36 changed files with 157 additions and 157 deletions

View File

@@ -8,7 +8,7 @@ This backlog tracks what must change so the implementation matches `docs/design.
- Existing tests cover older behavior. They are useful regression scaffolding, but they do not prove the latest design rules.
- The simulation has a working `SimulationEngine`, network propagation, consumers, structural integrity, leaks, reactor readiness, forecasts, serialization, editor helpers, and Godot session bridge.
- Godot currently has a usable UX scaffold and grid renderer, but it still exposes older interaction concepts such as a player-facing `EndTurn` action and always-available underground layer controls.
- Existing campaign data is the older three-level placeholder set: `coolant_restart.json`, `fuel_bleed.json`, and `black_start.json`. These levels and manifest entries must be replaced by the tutorial plus six-group campaign from `docs/CAMPAIGN.md`.
- Existing campaign data is the older three-level placeholder set: `water_restart.json`, `fuel_bleed.json`, and `black_start.json`. These levels and manifest entries must be replaced by the tutorial plus six-group campaign from `docs/CAMPAIGN.md`.
- Unrelated Godot metadata or generated `.uid` files are not part of this backlog unless a later implementation task intentionally touches them.
## P0 Simulation Contract
@@ -27,34 +27,34 @@ This backlog tracks what must change so the implementation matches `docs/design.
- Block intentional underground propagation across the authored branch boundary.
- Preserve downstream starvation/readiness consequences for consumers and reactor feed.
- Add editor placement, validation, serialization, and rendering support.
- [ ] Add `SprinklerControlProp` and wall-mounted `CoolantSprinklerValve`.
- A `SprinklerControlProp` links to exactly one `CoolantSprinklerValve`.
- [ ] Add `SprinklerControlProp` and wall-mounted `SprinklerValve`.
- A `SprinklerControlProp` links to exactly one `SprinklerValve`.
- The valve is wall-mounted, not directly interactive, and has exactly one outlet/access floor face.
- Discharge creates `SprinklerWater` only while linked control is `Enabled` and the coolant branch is fed.
- Discharge applies deterministic local coolant pressure debt.
- Discharge creates `Water` only while linked control is `Enabled` and the water branch is fed.
- Discharge applies deterministic local water pressure debt.
- Add editor placement/linking, validation, serialization, and rendering support.
- [ ] Rework surface coolant into `SprinklerWater`.
- Rename code-facing concepts where practical; otherwise make names and UI text consistently mean sprinkler water, not a generic coolant hazard.
- `Coolant` pipe failures should inject `SprinklerWater`, not a second damaging liquid.
- `SprinklerWater` alone must not cause `UnsafeEntryLoss`.
- [ ] Rework surface water into `Water`.
- Rename code-facing concepts where practical; otherwise make names and UI text consistently mean water, not a generic hazard.
- `water` pipe failures should inject `Water`, not a damaging liquid.
- `Water` alone must not cause `UnsafeEntryLoss`.
- [ ] Update leak injection.
- Leaks inject only when the underground leak cell has positive amount and positive pressure/voltage after propagation.
- Isolating a leak stops fresh injection without repairing the underlying fault.
- Repair restores structural integrity and stops future injection but does not clean existing surface values.
- [ ] Implement the approved surface interaction order.
- Resolve leak/sprinkler injection per `Step`.
- Resolve coolant mitigation before ignition and electrical spread.
- Resolve water mitigation before ignition and electrical spread.
- Implement `Dilute`, `Quench`, value-based `Evaporate`, wet-electric `Conduct`, and `Ignite`.
- Preserve deterministic same-cell and adjacent-cell delta accumulation.
- Closed powered doors and remedy blocks must gate only the interactions they explicitly block.
- [ ] Implement value-based evaporation.
- Add balance values for ambient evaporation, heat-driven evaporation, and evaporation cooling.
- Hot cells should evaporate `SprinklerWater` faster than cold cells.
- Hot cells should evaporate `Water` faster than cold cells.
- Evaporation happens during useful action pulses; there is no campaign wait command.
- [ ] Implement `Unsafe` as derived movement safety.
- `Unsafe` is recalculated after authored setup and after each `Pulse`.
- `Unsafe` is caused by unsafe `Heat`, unsafe `LeakedElectricity`, or the wet-electric unsafe rule.
- `LeakedFuel` alone and `SprinklerWater` alone are not `Unsafe`.
- `LeakedFuel` alone and `Water` alone are not `Unsafe`.
- `UnsafeEntryLoss` happens only when `MoveRobot` enters an `Unsafe` destination without applicable protection.
- A `Pulse` must not kill a stationary robot just because the current cell becomes `Unsafe`.
- [ ] Implement powered prop behavior.
@@ -72,7 +72,7 @@ This backlog tracks what must change so the implementation matches `docs/design.
## P0 Simulation Tests
- [ ] Update existing tests so their names and assertions use `Pulse`, `Step`, `SprinklerWater`, `Unsafe`, and `ReactorReadiness` terminology.
- [ ] Update existing tests so their names and assertions use `Pulse`, `Step`, `Water`, `Unsafe`, and `ReactorReadiness` terminology.
- [ ] Add tests for fixed pulse length.
- Every accepted `LengthyAction` advances one `Pulse`.
- Each pulse resolves the configured number of `Step`s.
@@ -84,18 +84,18 @@ This backlog tracks what must change so the implementation matches `docs/design.
- Open valve allows branch propagation.
- Closed valve isolates damaged branches and can starve downstream consumers/reactor feed.
- Toggling a valve triggers exactly one `Pulse`.
- [ ] Add tests for `SprinklerControlProp` and `CoolantSprinklerValve`.
- Valve discharge requires a linked enabled control and fed coolant branch.
- [ ] Add tests for `SprinklerControlProp` and `SprinklerValve`.
- Valve discharge requires a linked enabled control and fed water branch.
- Direct valve interaction is invalid or unavailable.
- Discharge creates `SprinklerWater` at the authored outlet and applies pressure debt.
- Discharge creates `Water` at the authored outlet and applies pressure debt.
- Disabling the linked control or isolating the sprinkler branch stops fresh discharge.
- [ ] Add tests for updated leak injection.
- Fed fuel, coolant, and electricity leaks inject to the correct access face.
- Fed fuel, water, and electricity leaks inject to the correct access face.
- Isolated leaks stop new surface injection.
- Repairs restore structural integrity without cleaning existing surface values.
- [ ] Add tests for surface interactions.
- `SprinklerWater` dilutes `LeakedFuel`.
- `SprinklerWater` quenches `Heat`.
- `Water` dilutes `LeakedFuel`.
- `Water` quenches `Heat`.
- Evaporation removes water and cools heat according to balance values.
- Wet cells conduct electricity faster than dry cells.
- Fuel plus electricity or heat can ignite and create heat while consuming fuel.
@@ -126,7 +126,7 @@ This backlog tracks what must change so the implementation matches `docs/design.
- [ ] Update serialization round trips for new fields.
- `IsolationValveProp` carrier and open/closed state.
- `SprinklerControlProp` enabled/disabled state and linked valve id or position.
- `CoolantSprinklerValve` wall position, outlet/access face, linked control, and coolant connection.
- `SprinklerValve` wall position, outlet/access face, linked control, and water connection.
- Powered terminal active state if it becomes serialized runtime state.
- [ ] Update `LevelEditor` tools.
- Add isolation valve placement.
@@ -137,7 +137,7 @@ This backlog tracks what must change so the implementation matches `docs/design.
- [ ] Update `LevelValidator`.
- Validate powered doors have valid geometry and local electricity.
- Validate terminal power requirements where needed.
- Validate wall-mounted sprinkler valve geometry, outlet/access face, coolant connection, and exactly one linked control.
- Validate wall-mounted sprinkler valve geometry, outlet/access face, water connection, and exactly one linked control.
- Validate isolation valves sit on exactly one matching underground carrier.
- Warn on initially unready reactors, initially starved required consumers, unused supplies, and sprinkler valves with no useful suppression or pressure tradeoff.
- [ ] Replace placeholder campaign files.
@@ -167,7 +167,7 @@ This backlog tracks what must change so the implementation matches `docs/design.
- Show why terminal access is unavailable when unpowered or away from the terminal.
- Use `Pulse +N` wording in forecast UI.
- [ ] Update grid rendering and inspector text.
- Render `SprinklerWater` separately from underground `Coolant`.
- Render `Water` separately from underground `water`.
- Render `Unsafe` cells with a distinct movement warning treatment.
- Render isolation valve state, sprinkler control state, wall-mounted sprinkler outlet, powered door state, and powered terminal state.
- Inspector should display visible surface values, prop state, consumer per-carrier state, and underground values only when terminal access allows it.
@@ -182,7 +182,7 @@ This backlog tracks what must change so the implementation matches `docs/design.
- [ ] Add campaign completion flow for the final Group 6 level.
- [ ] Add loading and malformed-level error states for campaign level loading.
- [ ] Verify Win2D editor export and Godot loader compatibility for the new schema.
- [ ] Revisit art labels/icons for `SprinklerWater`, `Unsafe`, powered props, and sprinkler controls after mechanics are implemented.
- [ ] Revisit art labels/icons for `Water`, `Unsafe`, powered props, and sprinkler controls after mechanics are implemented.
## Verification Rules

View File

@@ -23,7 +23,7 @@ public partial class CellInspector : PanelContainer
m_Text.AutowrapMode = TextServer.AutowrapMode.WordSmart;
}
public void SetCellInfo(GridPosition? position, ECellTerrain terrain, EPropType prop, EConsumerServiceState serviceState, float fuelHazard, float coolantHazard, float electricityHazard, float heatHazard)
public void SetCellInfo(GridPosition? position, ECellTerrain terrain, EPropType prop, EConsumerServiceState serviceState, float fuelHazard, float waterHazard, float electricityHazard, float heatHazard)
{
var pos = position ?? new(-1, -1);
var sb = new StringBuilder();
@@ -32,15 +32,15 @@ public partial class CellInspector : PanelContainer
sb.AppendLine($"Prop: {prop}");
if (prop != EPropType.None)
sb.AppendLine($"Service: {serviceState}");
sb.AppendLine($"Hazards: {FormatHazards(fuelHazard, coolantHazard, electricityHazard, heatHazard)}");
sb.AppendLine($"Hazards: {FormatHazards(fuelHazard, waterHazard, electricityHazard, heatHazard)}");
m_Text.Text = sb.ToString();
}
private static string FormatHazards(float fuel, float coolant, float electricity, float heat)
private static string FormatHazards(float fuel, float water, float electricity, float heat)
{
var parts = new List<string>();
if (fuel > 0) parts.Add($"fuel {fuel:F1}");
if (coolant > 0) parts.Add($"coolant {coolant:F1}");
if (water > 0) parts.Add($"water {water:F1}");
if (electricity > 0) parts.Add($"electricity {electricity:F1}");
if (heat > 0) parts.Add($"heat {heat:F1}");
return parts.Count > 0 ? string.Join(", ", parts) : "none";

View File

@@ -20,7 +20,7 @@ internal static class FrontendAssets
return ResourceLoader.Exists(path) ? ResourceLoader.Load<Texture2D>(path) : null;
}
public const string CoolantIcon = "res://Assets/Ui/coolant_icon.png";
public const string WaterIcon = "res://Assets/Ui/water_icon.png";
public const string ElectricIcon = "res://Assets/Ui/electric_icon.png";
public const string FuelIcon = "res://Assets/Ui/fuel_icon.png";
public const string HeatShieldIcon = "res://Assets/Ui/heat_shield_icon.png";

View File

@@ -147,7 +147,7 @@ public partial class GridViewport : Control
private IEnumerable<ECarrierType> OrderedUndergroundLayers()
{
var carriers = new[] { ECarrierType.Fuel, ECarrierType.Coolant, ECarrierType.Electricity }.Where(IsLayerVisible).ToArray();
var carriers = new[] { ECarrierType.Fuel, ECarrierType.Water, ECarrierType.Electricity }.Where(IsLayerVisible).ToArray();
return ActiveUndergroundLayer is { } activeCarrier && carriers.Contains(activeCarrier)
? carriers.Where(carrier => carrier != activeCarrier).Append(activeCarrier)
: carriers;
@@ -200,7 +200,7 @@ public partial class GridViewport : Control
var surface = m_LevelState.GetSurface(position);
var rect = layout.CellRect(position);
FillHazard(rect, surface.Fuel, c_FuelColor, 0.08f, opacity, Balancing.Current.FuelCaution, Balancing.Current.FuelCritical);
FillHazard(rect, surface.Coolant, c_CoolantColor, 0.18f, opacity, Balancing.Current.CoolantCaution, Balancing.Current.CoolantCritical);
FillHazard(rect, surface.Water, c_WaterColor, 0.18f, opacity, Balancing.Current.WaterCaution, Balancing.Current.WaterCritical);
FillHazard(rect, surface.Electricity, c_ElectricityColor, 0.28f, opacity, Balancing.Current.ElectricityCaution, Balancing.Current.ElectricityCritical);
FillHazard(rect, surface.Heat, c_HeatColor, 0.34f, opacity, Balancing.Current.HeatCaution, Balancing.Current.HeatCritical);
}
@@ -427,7 +427,7 @@ public partial class GridViewport : Control
{
return carrier switch {
ECarrierType.Fuel => ShowFuelLayer,
ECarrierType.Coolant => ShowCoolantLayer,
ECarrierType.Water => ShowWaterLayer,
ECarrierType.Electricity => ShowElectricityLayer,
_ => false
};
@@ -501,7 +501,7 @@ public partial class GridViewport : Control
{
return carrier switch {
ECarrierType.Fuel => c_FuelColor,
ECarrierType.Coolant => c_CoolantColor,
ECarrierType.Water => c_WaterColor,
ECarrierType.Electricity => c_ElectricityColor,
_ => Colors.White
};
@@ -537,7 +537,7 @@ public partial class GridViewport : Control
{
return carrier switch {
ECarrierType.Fuel => "F",
ECarrierType.Coolant => "C",
ECarrierType.Water => "C",
ECarrierType.Electricity => "E",
_ => "?"
};
@@ -547,7 +547,7 @@ public partial class GridViewport : Control
{
return remedy switch {
ERemedyType.FuelNeutralizer => "F REM",
ERemedyType.CoolantNeutralizer => "C REM",
ERemedyType.WaterNeutralizer => "C REM",
ERemedyType.ElectricityNeutralizer => "E REM",
ERemedyType.HeatShield => "H SHD",
_ => "REM"
@@ -588,7 +588,7 @@ public partial class GridViewport : Control
public Vector2I HoveredCell { get; private set; } = c_InvalidCell;
public Vector2I RobotPosition { get; private set; } = c_InvalidCell;
public bool ShowFuelLayer { get; set; } = true;
public bool ShowCoolantLayer { get; set; } = true;
public bool ShowWaterLayer { get; set; } = true;
public bool ShowElectricityLayer { get; set; } = true;
public ECarrierType? ActiveUndergroundLayer { get; set; }
@@ -611,7 +611,7 @@ public partial class GridViewport : Control
private const int c_AllCorners = c_TopLeftCorner | c_TopRightCorner | c_BottomLeftCorner | c_BottomRightCorner;
private static readonly Vector2I c_InvalidCell = new(-1, -1);
private static readonly Color c_BackgroundColor = new(0.06f, 0.07f, 0.08f);
private static readonly Color c_CoolantColor = new(0.20f, 0.78f, 0.92f);
private static readonly Color c_WaterColor = new(0.20f, 0.78f, 0.92f);
private static readonly Color c_DisabledColor = new(0.32f, 0.34f, 0.35f);
private static readonly Color c_ElectricityColor = new(0.96f, 0.78f, 0.20f);
private static readonly Color c_FuelColor = new(0.86f, 0.20f, 0.18f);

View File

@@ -7,19 +7,19 @@ public partial class InventoryStrip : HBoxContainer
public override void _Ready()
{
AddChild(CreateItem("Fuel Neutralizer", 2, FrontendAssets.FuelIcon, out var fuelLabel));
AddChild(CreateItem("Coolant Neutralizer", 2, FrontendAssets.CoolantIcon, out var coolantLabel));
AddChild(CreateItem("Water Neutralizer", 2, FrontendAssets.WaterIcon, out var waterLabel));
AddChild(CreateItem("Electric Neutralizer", 1, FrontendAssets.ElectricIcon, out var electricLabel));
AddChild(CreateItem("Heat Shield", 1, FrontendAssets.HeatShieldIcon, out var heatLabel));
m_FuelLabel = fuelLabel;
m_CoolantLabel = coolantLabel;
m_WaterLabel = waterLabel;
m_ElectricLabel = electricLabel;
m_HeatLabel = heatLabel;
}
public void SetInventory(int fuelNeutralizers, int coolantNeutralizers, int electricNeutralizers, int heatShields)
public void SetInventory(int fuelNeutralizers, int waterNeutralizers, int electricNeutralizers, int heatShields)
{
m_FuelLabel.Text = $"{fuelNeutralizers}";
m_CoolantLabel.Text = $"{coolantNeutralizers}";
m_WaterLabel.Text = $"{waterNeutralizers}";
m_ElectricLabel.Text = $"{electricNeutralizers}";
m_HeatLabel.Text = $"{heatShields}";
}
@@ -40,7 +40,7 @@ public partial class InventoryStrip : HBoxContainer
return item;
}
private Label m_CoolantLabel = null!;
private Label m_WaterLabel = null!;
private Label m_ElectricLabel = null!;
private Label m_FuelLabel = null!;

View File

@@ -113,7 +113,7 @@ public sealed class GameSession
public ECellTerrain[] Terrain => LevelState.Terrain;
public SurfaceState[] Surface => LevelState.Surface;
public UndergroundCell[] UndergroundFuel => LevelState.Fuel;
public UndergroundCell[] UndergroundCoolant => LevelState.Coolant;
public UndergroundCell[] UndergroundWater => LevelState.Water;
public UndergroundCell[] UndergroundElectricity => LevelState.Electricity;
public delegate void StateChangedHandler(GameSession sender);

View File

@@ -82,10 +82,10 @@ public partial class LevelScreen : ScreenBase
m_GridViewport.ShowFuelLayer = pressed;
m_GridViewport.QueueRedraw();
}));
controls.AddChild(CreateLayerToggle("Coolant", true, pressed => {
controls.AddChild(CreateLayerToggle("Water", true, pressed => {
if (m_GridViewport is null) return;
m_GridViewport.ShowCoolantLayer = pressed;
m_GridViewport.ShowWaterLayer = pressed;
m_GridViewport.QueueRedraw();
}));
controls.AddChild(CreateLayerToggle("Electricity", true, pressed => {
@@ -98,14 +98,14 @@ public partial class LevelScreen : ScreenBase
var activeLayer = new OptionButton();
activeLayer.AddItem("Surface", 0);
activeLayer.AddItem("Fuel", 1);
activeLayer.AddItem("Coolant", 2);
activeLayer.AddItem("Water", 2);
activeLayer.AddItem("Electricity", 3);
activeLayer.ItemSelected += index => {
if (m_GridViewport is null) return;
m_GridViewport.ActiveUndergroundLayer = index switch {
1 => ECarrierType.Fuel,
2 => ECarrierType.Coolant,
2 => ECarrierType.Water,
3 => ECarrierType.Electricity,
_ => null
};
@@ -187,7 +187,7 @@ public partial class LevelScreen : ScreenBase
m_InventoryStrip.SetInventory(
m_Session.LevelState.Robot.FuelNeutralizers,
m_Session.LevelState.Robot.CoolantNeutralizers,
m_Session.LevelState.Robot.WaterNeutralizers,
m_Session.LevelState.Robot.ElectricityNeutralizers,
m_Session.LevelState.Robot.HeatShields);
}
@@ -213,7 +213,7 @@ public partial class LevelScreen : ScreenBase
prop.Type,
prop.ServiceState,
surface.Fuel,
surface.Coolant,
surface.Water,
surface.Electricity,
surface.Heat);
}

View File

@@ -46,10 +46,10 @@ public abstract class Balancing
if (rowCarrier == ECarrierType.Fuel && colCarrier is null)
return rowBand == EBand.Critical || colBand == EBand.Critical ? Ignite(rowBand, colBand) : Warm(rowBand, colBand);
if (rowCarrier == ECarrierType.Coolant && colCarrier == ECarrierType.Electricity)
if (rowCarrier == ECarrierType.Water && colCarrier == ECarrierType.Electricity)
return Short(rowBand, colBand);
if (rowCarrier == ECarrierType.Coolant && colCarrier is null)
if (rowCarrier == ECarrierType.Water && colCarrier is null)
return Quench(rowBand, colBand);
return SurfaceInteractionEffect.Hold;
@@ -64,7 +64,7 @@ public abstract class Balancing
{
return carrier switch {
ECarrierType.Fuel => FuelCritical,
ECarrierType.Coolant => CoolantCritical,
ECarrierType.Water => WaterCritical,
ECarrierType.Electricity => ElectricityCritical,
_ => MaxValue
};
@@ -126,9 +126,9 @@ public abstract class Balancing
public abstract float FuelSafe { get; }
public abstract float FuelCaution { get; }
public abstract float FuelCritical { get; }
public abstract float CoolantSafe { get; }
public abstract float CoolantCaution { get; }
public abstract float CoolantCritical { get; }
public abstract float WaterSafe { get; }
public abstract float WaterCaution { get; }
public abstract float WaterCritical { get; }
public abstract float ElectricitySafe { get; }
public abstract float ElectricityCaution { get; }
public abstract float ElectricityCritical { get; }
@@ -137,7 +137,7 @@ public abstract class Balancing
public abstract float HeatCritical { get; }
public abstract float TerminalHeat { get; }
public abstract float RobotFuelSafetyThreshold { get; }
public abstract float RobotCoolantSafetyThreshold { get; }
public abstract float RobotWaterSafetyThreshold { get; }
public abstract float RobotElectricitySafetyThreshold { get; }
public abstract float RobotHeatSafetyThreshold { get; }
public abstract float SourceAmount { get; }

View File

@@ -11,9 +11,9 @@ public class NormalBalancing : Balancing
public override float FuelSafe => 1.5f;
public override float FuelCaution => 3.5f;
public override float FuelCritical => 6.5f;
public override float CoolantSafe => 1.5f;
public override float CoolantCaution => 3.5f;
public override float CoolantCritical => 6.5f;
public override float WaterSafe => 1.5f;
public override float WaterCaution => 3.5f;
public override float WaterCritical => 6.5f;
public override float ElectricitySafe => 1.5f;
public override float ElectricityCaution => 3.5f;
public override float ElectricityCritical => 6.5f;
@@ -22,7 +22,7 @@ public class NormalBalancing : Balancing
public override float HeatCritical => 8;
public override float TerminalHeat => 10;
public override float RobotFuelSafetyThreshold => 6.5f;
public override float RobotCoolantSafetyThreshold => 8;
public override float RobotWaterSafetyThreshold => 8;
public override float RobotElectricitySafetyThreshold => 6.5f;
public override float RobotHeatSafetyThreshold => 8;
public override float SourceAmount => 8;

View File

@@ -73,10 +73,10 @@ public static class LevelEditor
private static MoveOccupantResult TryMoveLeak(LevelState level, LeakState leak, GridPosition destination)
{
if (leak.Carrier is ECarrierType.Fuel or ECarrierType.Coolant)
if (leak.Carrier is ECarrierType.Fuel or ECarrierType.Water)
{
if (!level.IsFloor(destination))
return MoveOccupantResult.Failed(level, "Fuel and coolant leaks must move to a floor cell.");
return MoveOccupantResult.Failed(level, "Fuel and water leaks must move to a floor cell.");
var next = ClearLeak(level, leak)
.SetUnderground(leak.UndergroundPosition, leak.Carrier, new());
@@ -158,7 +158,7 @@ public static class LevelEditor
if (!level.InBounds(undergroundPosition) || !level.IsFloor(accessPosition))
return level;
if (carrier is ECarrierType.Fuel or ECarrierType.Coolant && undergroundPosition != accessPosition)
if (carrier is ECarrierType.Fuel or ECarrierType.Water && undergroundPosition != accessPosition)
return level;
if (carrier == ECarrierType.Electricity && undergroundPosition.ManhattanDistance(accessPosition) != 1)
@@ -198,7 +198,7 @@ public static class LevelEditor
var surface = level.GetSurface(position);
return carrier switch {
ECarrierType.Fuel => level.SetSurface(position, surface with { Fuel = surface.Fuel + 1 }),
ECarrierType.Coolant => level.SetSurface(position, surface with { Coolant = surface.Coolant + 1 }),
ECarrierType.Water => level.SetSurface(position, surface with { Water = surface.Water + 1 }),
ECarrierType.Electricity => level.SetSurface(position, surface with { Electricity = surface.Electricity + 1 }),
_ => level
};

View File

@@ -59,7 +59,7 @@ public static class LevelStateExtensions
next[level.Index(position)] = cell;
return carrier switch {
ECarrierType.Fuel => level with { Fuel = next },
ECarrierType.Coolant => level with { Coolant = next },
ECarrierType.Water => level with { Water = next },
ECarrierType.Electricity => level with { Electricity = next },
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};
@@ -79,11 +79,11 @@ public static class LevelStateExtensions
return level with { Props = next };
}
public static LevelState WithRuntimeArrays(this LevelState level, UndergroundCell[] fuel, UndergroundCell[] coolant, UndergroundCell[] electricity, SurfaceState[] surface, PropState[] props)
public static LevelState WithRuntimeArrays(this LevelState level, UndergroundCell[] fuel, UndergroundCell[] water, UndergroundCell[] electricity, SurfaceState[] surface, PropState[] props)
{
return level with {
Fuel = fuel,
Coolant = coolant,
Water = water,
Electricity = electricity,
Surface = surface,
Props = props
@@ -94,7 +94,7 @@ public static class LevelStateExtensions
{
return carrier switch {
ECarrierType.Fuel => level.Fuel,
ECarrierType.Coolant => level.Coolant,
ECarrierType.Water => level.Water,
ECarrierType.Electricity => level.Electricity,
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};

View File

@@ -13,7 +13,7 @@ public static class LevelStateFactory
Height = height,
Terrain = CreateTerrain(width, height),
Fuel = CreateUnderground(width, height),
Coolant = CreateUnderground(width, height),
Water = CreateUnderground(width, height),
Electricity = CreateUnderground(width, height),
Surface = CreateSurface(width, height),
Props = CreateProps(width, height),

View File

@@ -25,7 +25,7 @@ public sealed class LevelValidator
errors.Add(new("Invalid level dimensions."));
var expected = level.Width * level.Height;
if (level.Terrain.Length != expected || level.Fuel.Length != expected || level.Coolant.Length != expected || level.Electricity.Length != expected || level.Surface.Length != expected || level.Props.Length != expected)
if (level.Terrain.Length != expected || level.Fuel.Length != expected || level.Water.Length != expected || level.Electricity.Length != expected || level.Surface.Length != expected || level.Props.Length != expected)
errors.Add(new("Cell array counts do not match level dimensions."));
}
@@ -47,7 +47,7 @@ public sealed class LevelValidator
if (level.GetTerrain(position) == ECellTerrain.Wall)
{
if (surface.Fuel > 0 || surface.Coolant > 0 || surface.Electricity > 0 || surface.Heat > 0)
if (surface.Fuel > 0 || surface.Water > 0 || surface.Electricity > 0 || surface.Heat > 0)
errors.Add(new("Wall cell cannot store surface hazards.", position));
if (prop.Type != EPropType.None)
@@ -91,8 +91,8 @@ public sealed class LevelValidator
if (!underground.IsPresent)
errors.Add(new("Leak target must point to an underground cell.", leak.UndergroundPosition));
if (leak.Carrier is ECarrierType.Fuel or ECarrierType.Coolant && leak.UndergroundPosition != leak.AccessPosition)
errors.Add(new("Fuel and coolant leaks must use their underground coordinate as access.", leak.AccessPosition));
if (leak.Carrier is ECarrierType.Fuel or ECarrierType.Water && leak.UndergroundPosition != leak.AccessPosition)
errors.Add(new("Fuel and water leaks must use their underground coordinate as access.", leak.AccessPosition));
if (leak.Carrier == ECarrierType.Electricity && leak.UndergroundPosition.ManhattanDistance(leak.AccessPosition) != 1)
errors.Add(new("Electricity leak access must be an adjacent floor face.", leak.AccessPosition));
@@ -110,7 +110,7 @@ public sealed class LevelValidator
warnings.Add(new("Reactor is initially unready.", reactor.ControlPosition));
}
if (level.RequiredFuelConsumers < 0 || level.RequiredCoolantConsumers < 0 || level.RequiredElectricityConsumers < 0)
if (level.RequiredFuelConsumers < 0 || level.RequiredWaterConsumers < 0 || level.RequiredElectricityConsumers < 0)
errors.Add(new("Required consumer counts cannot be negative."));
}

View File

@@ -3,6 +3,6 @@
public enum ECarrierType
{
Fuel,
Coolant,
Water,
Electricity
}

View File

@@ -3,7 +3,7 @@
public enum ERemedyType
{
FuelNeutralizer,
CoolantNeutralizer,
WaterNeutralizer,
ElectricityNeutralizer,
HeatShield
}

View File

@@ -3,7 +3,7 @@
public enum ESurfaceQuantity
{
Fuel,
Coolant,
Water,
Electricity,
Heat
}

View File

@@ -12,14 +12,14 @@ public sealed record LevelState
public int Height { get; init; } = Balancing.Current.DefaultLevelHeight;
public ECellTerrain[] Terrain { get; init; } = LevelStateFactory.CreateTerrain(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public UndergroundCell[] Fuel { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public UndergroundCell[] Coolant { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public UndergroundCell[] Water { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public UndergroundCell[] Electricity { get; init; } = LevelStateFactory.CreateUnderground(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public SurfaceState[] Surface { get; init; } = LevelStateFactory.CreateSurface(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public PropState[] Props { get; init; } = LevelStateFactory.CreateProps(Balancing.Current.DefaultLevelWidth, Balancing.Current.DefaultLevelHeight);
public IReadOnlyList<LeakState> Leaks { get; init; } = Array.Empty<LeakState>();
public IReadOnlyList<ReactorState> Reactors { get; init; } = Array.Empty<ReactorState>();
public int RequiredFuelConsumers { get; init; } = 1;
public int RequiredCoolantConsumers { get; init; } = 1;
public int RequiredWaterConsumers { get; init; } = 1;
public int RequiredElectricityConsumers { get; init; } = 1;
public RobotState Robot { get; init; } = new();
public GlobalState Global { get; init; } = new();

View File

@@ -6,7 +6,7 @@ public sealed record PropState
{
return carrier switch {
ECarrierType.Fuel => FuelServiceState,
ECarrierType.Coolant => CoolantServiceState,
ECarrierType.Water => WaterServiceState,
ECarrierType.Electricity => ElectricityServiceState,
_ => EConsumerServiceState.Unknown
};
@@ -17,7 +17,7 @@ public sealed record PropState
public EPropSwitchState SwitchState { get; init; } = EPropSwitchState.Enabled;
public EConsumerServiceState ServiceState { get; init; } = EConsumerServiceState.Unknown;
public EConsumerServiceState FuelServiceState { get; init; } = EConsumerServiceState.Unknown;
public EConsumerServiceState CoolantServiceState { get; init; } = EConsumerServiceState.Unknown;
public EConsumerServiceState WaterServiceState { get; init; } = EConsumerServiceState.Unknown;
public EConsumerServiceState ElectricityServiceState { get; init; } = EConsumerServiceState.Unknown;
public int JunctionMode { get; init; }
public ERemedyType RemedyType { get; init; }

View File

@@ -4,7 +4,7 @@ public sealed record RobotState
{
public GridPosition Position { get; init; } = new(1, 1);
public int FuelNeutralizers { get; init; }
public int CoolantNeutralizers { get; init; }
public int WaterNeutralizers { get; init; }
public int ElectricityNeutralizers { get; init; }
public int HeatShields { get; init; }
public int HeatImmunitySteps { get; init; }

View File

@@ -3,10 +3,10 @@
public sealed record SurfaceState
{
public float Fuel { get; init; }
public float Coolant { get; init; }
public float Water { get; init; }
public float Electricity { get; init; }
public float Heat { get; init; }
public int FuelBlockTurns { get; init; }
public int CoolantBlockTurns { get; init; }
public int WaterBlockTurns { get; init; }
public int ElectricityBlockTurns { get; init; }
}

View File

@@ -6,7 +6,7 @@ public static class RobotStateExtensions
{
return remedy switch {
ERemedyType.FuelNeutralizer => robot.FuelNeutralizers,
ERemedyType.CoolantNeutralizer => robot.CoolantNeutralizers,
ERemedyType.WaterNeutralizer => robot.WaterNeutralizers,
ERemedyType.ElectricityNeutralizer => robot.ElectricityNeutralizers,
ERemedyType.HeatShield => robot.HeatShields,
_ => throw new ArgumentOutOfRangeException(nameof(remedy), remedy, "Unsupported remedy.")
@@ -17,7 +17,7 @@ public static class RobotStateExtensions
{
return remedy switch {
ERemedyType.FuelNeutralizer => robot with { FuelNeutralizers = ClampInventory(robot.FuelNeutralizers + amount) },
ERemedyType.CoolantNeutralizer => robot with { CoolantNeutralizers = ClampInventory(robot.CoolantNeutralizers + amount) },
ERemedyType.WaterNeutralizer => robot with { WaterNeutralizers = ClampInventory(robot.WaterNeutralizers + amount) },
ERemedyType.ElectricityNeutralizer => robot with { ElectricityNeutralizers = ClampInventory(robot.ElectricityNeutralizers + amount) },
ERemedyType.HeatShield => robot with { HeatShields = ClampInventory(robot.HeatShields + amount) },
_ => throw new ArgumentOutOfRangeException(nameof(remedy), remedy, "Unsupported remedy.")

View File

@@ -6,7 +6,7 @@ internal static class SimulationBands
{
return carrier switch {
ECarrierType.Fuel => Fuel(surface.Fuel),
ECarrierType.Coolant => Coolant(surface.Coolant),
ECarrierType.Water => Water(surface.Water),
ECarrierType.Electricity => Electricity(surface.Electricity),
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};
@@ -17,7 +17,7 @@ internal static class SimulationBands
var value = valueKind == ENetworkValueKind.Amount ? underground.Amount : underground.Intensity;
return carrier switch {
ECarrierType.Fuel => Fuel(value),
ECarrierType.Coolant => Coolant(value),
ECarrierType.Water => Water(value),
ECarrierType.Electricity => Electricity(value),
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};
@@ -28,9 +28,9 @@ internal static class SimulationBands
return Balancing.Current.Band(value, Balancing.Current.FuelCaution, Balancing.Current.FuelCritical);
}
public static EBand Coolant(float value)
public static EBand Water(float value)
{
return Balancing.Current.Band(value, Balancing.Current.CoolantCaution, Balancing.Current.CoolantCritical);
return Balancing.Current.Band(value, Balancing.Current.WaterCaution, Balancing.Current.WaterCritical);
}
public static EBand Electricity(float value)

View File

@@ -6,7 +6,7 @@ internal static class SurfaceCarrierMath
{
return carrier switch {
ECarrierType.Fuel => surface with { Fuel = surface.Fuel + amount },
ECarrierType.Coolant => surface with { Coolant = surface.Coolant + amount },
ECarrierType.Water => surface with { Water = surface.Water + amount },
ECarrierType.Electricity => surface with { Electricity = surface.Electricity + amount },
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};
@@ -16,7 +16,7 @@ internal static class SurfaceCarrierMath
{
return carrier switch {
ECarrierType.Fuel => surface with { Fuel = 0, FuelBlockTurns = Balancing.Current.RemedyBlockTurns },
ECarrierType.Coolant => surface with { Coolant = 0, CoolantBlockTurns = Balancing.Current.RemedyBlockTurns },
ECarrierType.Water => surface with { Water = 0, WaterBlockTurns = Balancing.Current.RemedyBlockTurns },
ECarrierType.Electricity => surface with { Electricity = 0, ElectricityBlockTurns = Balancing.Current.RemedyBlockTurns },
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};

View File

@@ -7,11 +7,11 @@ public static class SurfaceStateExtensions
var balancing = Balancing.Current;
return surface with {
Fuel = balancing.ClampValue(surface.Fuel),
Coolant = balancing.ClampValue(surface.Coolant),
Water = balancing.ClampValue(surface.Water),
Electricity = balancing.ClampValue(surface.Electricity),
Heat = balancing.ClampValue(surface.Heat),
FuelBlockTurns = Math.Max(0, surface.FuelBlockTurns),
CoolantBlockTurns = Math.Max(0, surface.CoolantBlockTurns),
WaterBlockTurns = Math.Max(0, surface.WaterBlockTurns),
ElectricityBlockTurns = Math.Max(0, surface.ElectricityBlockTurns)
};
}
@@ -20,7 +20,7 @@ public static class SurfaceStateExtensions
{
return carrier switch {
ECarrierType.Fuel => surface.FuelBlockTurns > 0,
ECarrierType.Coolant => surface.CoolantBlockTurns > 0,
ECarrierType.Water => surface.WaterBlockTurns > 0,
ECarrierType.Electricity => surface.ElectricityBlockTurns > 0,
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};

View File

@@ -15,24 +15,24 @@ internal static class ConsumerSystem
if (prop.SwitchState == EPropSwitchState.Disabled)
{
var disabledFuel = DisabledServiceStateFor(level, position, ECarrierType.Fuel);
var disabledCoolant = DisabledServiceStateFor(level, position, ECarrierType.Coolant);
var disabledWater = DisabledServiceStateFor(level, position, ECarrierType.Water);
var disabledElectricity = DisabledServiceStateFor(level, position, ECarrierType.Electricity);
props[index] = prop with {
ServiceState = Aggregate(disabledFuel, disabledCoolant, disabledElectricity),
ServiceState = Aggregate(disabledFuel, disabledWater, disabledElectricity),
FuelServiceState = disabledFuel,
CoolantServiceState = disabledCoolant,
WaterServiceState = disabledWater,
ElectricityServiceState = disabledElectricity
};
continue;
}
var fuel = ServiceStateFor(level, position, ECarrierType.Fuel);
var coolant = ServiceStateFor(level, position, ECarrierType.Coolant);
var water = ServiceStateFor(level, position, ECarrierType.Water);
var electricity = ServiceStateFor(level, position, ECarrierType.Electricity);
props[index] = prop with {
ServiceState = Aggregate(fuel, coolant, electricity),
ServiceState = Aggregate(fuel, water, electricity),
FuelServiceState = fuel,
CoolantServiceState = coolant,
WaterServiceState = water,
ElectricityServiceState = electricity
};
}

View File

@@ -25,7 +25,7 @@ internal static class ForecastSystem
return level with {
Terrain = level.Terrain.ToArray(),
Fuel = level.Fuel.ToArray(),
Coolant = level.Coolant.ToArray(),
Water = level.Water.ToArray(),
Electricity = level.Electricity.ToArray(),
Surface = level.Surface.ToArray(),
Props = level.Props.ToArray(),
@@ -61,7 +61,7 @@ internal static class ForecastSystem
}
var surface = level.GetSurface(position);
if (SimulationBands.Fuel(surface.Fuel) == EBand.Critical || SimulationBands.Coolant(surface.Coolant) == EBand.Critical || SimulationBands.Electricity(surface.Electricity) == EBand.Critical || SimulationBands.Heat(surface.Heat) == EBand.Critical)
if (SimulationBands.Fuel(surface.Fuel) == EBand.Critical || SimulationBands.Water(surface.Water) == EBand.Critical || SimulationBands.Electricity(surface.Electricity) == EBand.Critical || SimulationBands.Heat(surface.Heat) == EBand.Critical)
forecasts.Add(new(EForecastKind.HazardGrowth, position, turn, "Critical hazard"));
}
}

View File

@@ -5,9 +5,9 @@ internal static class NetworkPropagationSystem
public static LevelState Propagate(LevelState level)
{
var fuel = ClearTransient(level.Fuel);
var coolant = ClearTransient(level.Coolant);
var water = ClearTransient(level.Water);
var electricity = ClearTransient(level.Electricity);
var next = level.WithRuntimeArrays(fuel, coolant, electricity, level.Surface.ToArray(), level.Props.ToArray());
var next = level.WithRuntimeArrays(fuel, water, electricity, level.Surface.ToArray(), level.Props.ToArray());
foreach (var carrier in Enum.GetValues<ECarrierType>())
next = PropagateCarrier(next, carrier);
@@ -31,7 +31,7 @@ internal static class NetworkPropagationSystem
return carrier switch {
ECarrierType.Fuel => level with { Fuel = layer },
ECarrierType.Coolant => level with { Coolant = layer },
ECarrierType.Water => level with { Water = layer },
ECarrierType.Electricity => level with { Electricity = layer },
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};

View File

@@ -98,7 +98,7 @@ internal static class PlayerActionSystem
{
var remedy = leak.Carrier switch {
ECarrierType.Fuel => ERemedyType.FuelNeutralizer,
ECarrierType.Coolant => ERemedyType.CoolantNeutralizer,
ECarrierType.Water => ERemedyType.WaterNeutralizer,
ECarrierType.Electricity => ERemedyType.ElectricityNeutralizer,
_ => throw new ArgumentOutOfRangeException(nameof(leak), leak.Carrier, "Unsupported leak carrier.")
};

View File

@@ -33,7 +33,7 @@ internal static class ReactorSystem
if (maxHeat >= Balancing.Current.TerminalHeat)
return level with { Reactors = reactors, Global = level.Global with { LevelState = ELevelState.Lost, TerminalLoss = true, Status = "REACTOR HEAT TERMINAL" } };
var hasCritical = level.Surface.Any(surface => SimulationBands.Fuel(surface.Fuel) == EBand.Critical || SimulationBands.Coolant(surface.Coolant) == EBand.Critical || SimulationBands.Electricity(surface.Electricity) == EBand.Critical || SimulationBands.Heat(surface.Heat) == EBand.Critical);
var hasCritical = level.Surface.Any(surface => SimulationBands.Fuel(surface.Fuel) == EBand.Critical || SimulationBands.Water(surface.Water) == EBand.Critical || SimulationBands.Electricity(surface.Electricity) == EBand.Critical || SimulationBands.Heat(surface.Heat) == EBand.Critical);
var hasCaution = hasCritical
|| !HasRequiredConsumers(level)
|| level.Props.Any(prop => prop.Type == EPropType.Consumer && HasConsumerTrouble(prop))
@@ -47,7 +47,7 @@ internal static class ReactorSystem
{
return ReactorFeedsPresentAndProducing(level, reactor.ControlPosition)
&& ProducingConsumerCount(level, ECarrierType.Fuel) >= level.RequiredFuelConsumers
&& ProducingConsumerCount(level, ECarrierType.Coolant) >= level.RequiredCoolantConsumers
&& ProducingConsumerCount(level, ECarrierType.Water) >= level.RequiredWaterConsumers
&& ProducingConsumerCount(level, ECarrierType.Electricity) >= level.RequiredElectricityConsumers
&& level.GetSurface(reactor.ControlPosition).Heat < Balancing.Current.TerminalHeat;
}
@@ -73,14 +73,14 @@ internal static class ReactorSystem
private static bool HasRequiredConsumers(LevelState level)
{
return ProducingConsumerCount(level, ECarrierType.Fuel) >= level.RequiredFuelConsumers
&& ProducingConsumerCount(level, ECarrierType.Coolant) >= level.RequiredCoolantConsumers
&& ProducingConsumerCount(level, ECarrierType.Water) >= level.RequiredWaterConsumers
&& ProducingConsumerCount(level, ECarrierType.Electricity) >= level.RequiredElectricityConsumers;
}
private static bool HasConsumerTrouble(PropState prop)
{
return prop.FuelServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled
|| prop.CoolantServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled
|| prop.WaterServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled
|| prop.ElectricityServiceState is EConsumerServiceState.Starved or EConsumerServiceState.Disabled;
}

View File

@@ -5,7 +5,7 @@ internal static class RobotSafetySystem
public static LevelState Resolve(LevelState level)
{
var surface = level.GetSurface(level.Robot.Position);
var unsafeElement = surface.Fuel >= Balancing.Current.RobotFuelSafetyThreshold || surface.Coolant >= Balancing.Current.RobotCoolantSafetyThreshold || surface.Electricity >= Balancing.Current.RobotElectricitySafetyThreshold;
var unsafeElement = surface.Fuel >= Balancing.Current.RobotFuelSafetyThreshold || surface.Water >= Balancing.Current.RobotWaterSafetyThreshold || surface.Electricity >= Balancing.Current.RobotElectricitySafetyThreshold;
var unsafeHeat = surface.Heat >= Balancing.Current.RobotHeatSafetyThreshold && level.Robot.HeatImmunitySteps <= 0;
return unsafeElement || unsafeHeat
? level with { Global = level.Global with { LevelState = ELevelState.Lost, TerminalLoss = true, Status = "ROBOT LOST" } }

View File

@@ -35,7 +35,7 @@ internal static class StructuralIntegritySystem
var next = carrier switch {
ECarrierType.Fuel => level with { Fuel = layer },
ECarrierType.Coolant => level with { Coolant = layer },
ECarrierType.Water => level with { Water = layer },
ECarrierType.Electricity => level with { Electricity = layer },
_ => throw new ArgumentOutOfRangeException(nameof(carrier), carrier, "Unsupported carrier.")
};

View File

@@ -8,14 +8,14 @@ internal static class SurfaceInteractionSystem
{
return surface with {
Fuel = surface.Fuel + Fuel,
Coolant = surface.Coolant + Coolant,
Water = surface.Water + Water,
Electricity = surface.Electricity + Electricity,
Heat = surface.Heat + Heat
};
}
public float Fuel { get; set; }
public float Coolant { get; set; }
public float Water { get; set; }
public float Electricity { get; set; }
public float Heat { get; set; }
}
@@ -48,7 +48,7 @@ internal static class SurfaceInteractionSystem
{
var surface = level.Surface.Select(cell => cell with {
FuelBlockTurns = Math.Max(0, cell.FuelBlockTurns - 1),
CoolantBlockTurns = Math.Max(0, cell.CoolantBlockTurns - 1),
WaterBlockTurns = Math.Max(0, cell.WaterBlockTurns - 1),
ElectricityBlockTurns = Math.Max(0, cell.ElectricityBlockTurns - 1)
})
.ToArray();
@@ -61,8 +61,8 @@ internal static class SurfaceInteractionSystem
var surface = level.GetSurface(position);
ApplyPair(level, position, ECarrierType.Fuel, SimulationBands.Fuel(surface.Fuel), ECarrierType.Electricity, SimulationBands.Electricity(surface.Electricity), deltas);
ApplyPair(level, position, ECarrierType.Fuel, SimulationBands.Fuel(surface.Fuel), null, SimulationBands.Heat(surface.Heat), deltas);
ApplyPair(level, position, ECarrierType.Coolant, SimulationBands.Coolant(surface.Coolant), ECarrierType.Electricity, SimulationBands.Electricity(surface.Electricity), deltas);
ApplyPair(level, position, ECarrierType.Coolant, SimulationBands.Coolant(surface.Coolant), null, SimulationBands.Heat(surface.Heat), deltas);
ApplyPair(level, position, ECarrierType.Water, SimulationBands.Water(surface.Water), ECarrierType.Electricity, SimulationBands.Electricity(surface.Electricity), deltas);
ApplyPair(level, position, ECarrierType.Water, SimulationBands.Water(surface.Water), null, SimulationBands.Heat(surface.Heat), deltas);
}
private static void ApplyAdjacentInteractions(LevelState level, GridPosition a, GridPosition b, SurfaceDelta[] deltas)
@@ -70,7 +70,7 @@ internal static class SurfaceInteractionSystem
var surfaceA = level.GetSurface(a);
var surfaceB = level.GetSurface(b);
FlowBetween(level, a, b, surfaceA.Fuel, surfaceB.Fuel, Balancing.Current.FlowInteraction(ESurfaceQuantity.Fuel), deltas);
FlowBetween(level, a, b, surfaceA.Coolant, surfaceB.Coolant, Balancing.Current.FlowInteraction(ESurfaceQuantity.Coolant), deltas);
FlowBetween(level, a, b, surfaceA.Water, surfaceB.Water, Balancing.Current.FlowInteraction(ESurfaceQuantity.Water), deltas);
FlowBetween(level, a, b, surfaceA.Electricity, surfaceB.Electricity, Balancing.Current.FlowInteraction(ESurfaceQuantity.Electricity), deltas);
FlowBetween(level, a, b, surfaceA.Heat, surfaceB.Heat, Balancing.Current.FlowInteraction(ESurfaceQuantity.Heat), deltas);
}
@@ -118,9 +118,9 @@ internal static class SurfaceInteractionSystem
deltas[indexA].Fuel -= amount;
deltas[indexB].Fuel += amount;
break;
case ESurfaceQuantity.Coolant:
deltas[indexA].Coolant -= amount;
deltas[indexB].Coolant += amount;
case ESurfaceQuantity.Water:
deltas[indexA].Water -= amount;
deltas[indexB].Water += amount;
break;
case ESurfaceQuantity.Electricity:
deltas[indexA].Electricity -= amount;

View File

@@ -21,18 +21,18 @@ public sealed class EditorImageRegistry
AddAlias("prop-consumer", "generator");
AddAlias("prop-flow", "generator");
AddAlias("carrier-fuel-source", "generator");
AddAlias("carrier-coolant-source", "cooling-pump");
AddAlias("carrier-water-source", "cooling-pump");
AddAlias("carrier-electricity-source", "generator");
AddAlias("prop-junction", "pressure-regulator");
AddAlias("prop-door", "wall");
AddAlias("prop-eye-terminal", "diagnostic-terminal");
AddAlias("prop-remedy", "repair");
AddAlias("leak-fuel", "leak");
AddAlias("leak-coolant", "leak");
AddAlias("leak-water", "leak");
AddAlias("leak-electricity", "leak");
AddAlias("hazard-heat", "heat");
AddAlias("hazard-fuel", "fire");
AddAlias("hazard-coolant", "leak");
AddAlias("hazard-water", "leak");
AddAlias("hazard-electricity", "heat");
}

View File

@@ -27,7 +27,7 @@ public sealed partial class MainWindow
Surface,
Electricity,
Fuel,
Coolant
Water
}
private sealed record CanvasLayout(double CellSize, double OriginX, double OriginY)
@@ -624,7 +624,7 @@ public sealed partial class MainWindow
private IEnumerable<ECarrierType> OrderedUndergroundLayers()
{
var carriers = new[] { ECarrierType.Fuel, ECarrierType.Coolant, ECarrierType.Electricity };
var carriers = new[] { ECarrierType.Fuel, ECarrierType.Water, ECarrierType.Electricity };
var activeCarrier = LayerCarrier(m_ActiveLayer);
return activeCarrier is null ? carriers : carriers.Where(carrier => carrier != activeCarrier).Append(activeCarrier.Value);
}
@@ -679,7 +679,7 @@ public sealed partial class MainWindow
var surface = m_Level.GetSurface(position);
var rect = layout.CellRect(position);
FillHazard(drawing, rect, surface.Fuel, c_FuelColor, 0.08, opacity, Balancing.Current.FuelCaution, Balancing.Current.FuelCritical);
FillHazard(drawing, rect, surface.Coolant, c_CoolantColor, 0.18, opacity, Balancing.Current.CoolantCaution, Balancing.Current.CoolantCritical);
FillHazard(drawing, rect, surface.Water, c_WaterColor, 0.18, opacity, Balancing.Current.WaterCaution, Balancing.Current.WaterCritical);
FillHazard(drawing, rect, surface.Electricity, c_ElectricityColor, 0.28, opacity, Balancing.Current.ElectricityCaution, Balancing.Current.ElectricityCritical);
var heatOpacity = SurfaceOverlayOpacity(surface.Heat, Balancing.Current.HeatCaution, Balancing.Current.HeatCritical);
@@ -884,13 +884,13 @@ public sealed partial class MainWindow
: $"{m_Level.Global.LevelState}: {m_EditorFeedback}";
InventoryGrid.ItemsSource = new[] {
new InspectorItemViewModel("Fuel", $"{m_Level.Robot.FuelNeutralizers}"),
new InspectorItemViewModel("Coolant", $"{m_Level.Robot.CoolantNeutralizers}"),
new InspectorItemViewModel("Water", $"{m_Level.Robot.WaterNeutralizers}"),
new InspectorItemViewModel("Electricity", $"{m_Level.Robot.ElectricityNeutralizers}"),
new InspectorItemViewModel("Heat", $"{m_Level.Robot.HeatShields} ({m_Level.Robot.HeatImmunitySteps.ToString(CultureInfo.InvariantCulture)} steps)")
};
RequiredGrid.ItemsSource = new[] {
new InspectorItemViewModel("Fuel", m_Level.RequiredFuelConsumers.ToString(CultureInfo.InvariantCulture)),
new InspectorItemViewModel("Coolant", m_Level.RequiredCoolantConsumers.ToString(CultureInfo.InvariantCulture)),
new InspectorItemViewModel("Water", m_Level.RequiredWaterConsumers.ToString(CultureInfo.InvariantCulture)),
new InspectorItemViewModel("Electric", m_Level.RequiredElectricityConsumers.ToString(CultureInfo.InvariantCulture))
};
@@ -902,13 +902,13 @@ public sealed partial class MainWindow
PropText.Text = prop.Type != EPropType.None ? $"{prop.Type} {prop.SwitchState}" : "(none)";
ServicesGrid.ItemsSource = new[] {
new InspectorItemViewModel("Fuel", prop.FuelServiceState.ToString()),
new InspectorItemViewModel("Coolant", prop.CoolantServiceState.ToString()),
new InspectorItemViewModel("Water", prop.WaterServiceState.ToString()),
new InspectorItemViewModel("Electricity", prop.ElectricityServiceState.ToString())
};
var surface = m_Level.GetSurface(position);
ConsumersGrid.ItemsSource = new[] {
new InspectorItemViewModel("Fuel", surface.FuelBlockTurns.ToString()),
new InspectorItemViewModel("Coolant", surface.CoolantBlockTurns.ToString()),
new InspectorItemViewModel("Water", surface.WaterBlockTurns.ToString()),
new InspectorItemViewModel("Electricity", surface.ElectricityBlockTurns.ToString())
};
LeaksGrid.ItemsSource = m_Level.Leaks.Select(leak => new InspectorItemViewModel(leak.Carrier.ToString(), $"{leak.UndergroundPosition.X}, {leak.UndergroundPosition.Y}"));
@@ -959,7 +959,7 @@ public sealed partial class MainWindow
var surface = m_Level.GetSurface(position);
return [
new("Fuel", Format(surface.Fuel)),
new("Coolant", Format(surface.Coolant)),
new("Water", Format(surface.Water)),
new("Electric", Format(surface.Electricity)),
new("Heat", Format(surface.Heat))
];
@@ -969,7 +969,7 @@ public sealed partial class MainWindow
{
return [
NetworkInspectionItem("Fuel", m_Level.GetUnderground(position, ECarrierType.Fuel)),
NetworkInspectionItem("Coolant", m_Level.GetUnderground(position, ECarrierType.Coolant)),
NetworkInspectionItem("Water", m_Level.GetUnderground(position, ECarrierType.Water)),
NetworkInspectionItem("Electric", m_Level.GetUnderground(position, ECarrierType.Electricity))
];
}
@@ -993,7 +993,7 @@ public sealed partial class MainWindow
return $"{leak.Carrier} Leak";
var surface = m_Level.GetSurface(position);
if (surface.Fuel > 0 || surface.Coolant > 0 || surface.Electricity > 0)
if (surface.Fuel > 0 || surface.Water > 0 || surface.Electricity > 0)
return "Surface Hazard";
if (surface.Heat > 0)
@@ -1011,10 +1011,10 @@ public sealed partial class MainWindow
{
var level = LevelState.Create("Cooling Sector B", 16, 12);
level = AddNetwork(level, ECarrierType.Fuel, new(2, 3), new(5, 3));
level = AddNetwork(level, ECarrierType.Coolant, new(2, 5), new(5, 5));
level = AddNetwork(level, ECarrierType.Water, new(2, 5), new(5, 5));
level = AddNetwork(level, ECarrierType.Electricity, new(2, 7), new(5, 7));
level = level.SetProp(new(2, 3), new() { Type = EPropType.Flow, Carrier = ECarrierType.Fuel });
level = level.SetProp(new(2, 5), new() { Type = EPropType.Flow, Carrier = ECarrierType.Coolant });
level = level.SetProp(new(2, 5), new() { Type = EPropType.Flow, Carrier = ECarrierType.Water });
level = level.SetProp(new(2, 7), new() { Type = EPropType.Flow, Carrier = ECarrierType.Electricity });
level = level.SetProp(new(5, 3), new() { Type = EPropType.Consumer });
level = level.SetProp(new(5, 5), new() { Type = EPropType.Consumer });
@@ -1022,8 +1022,8 @@ public sealed partial class MainWindow
level = level.SetProp(new(10, 5), new() { Type = EPropType.ReactorControl, ReactorId = 1 });
level = level.SetProp(new(11, 4), new() { Type = EPropType.AllSeeingEyeTerminal });
level = level.SetProp(new(11, 6), new() { Type = EPropType.RemedySupply, RemedyType = ERemedyType.HeatShield });
level = level.SetUnderground(new(4, 5), ECarrierType.Coolant, new() { State = EUndergroundState.Leaking }) with {
Leaks = [new() { Carrier = ECarrierType.Coolant, UndergroundPosition = new(4, 5), AccessPosition = new(4, 5) }],
level = level.SetUnderground(new(4, 5), ECarrierType.Water, new() { State = EUndergroundState.Leaking }) with {
Leaks = [new() { Carrier = ECarrierType.Water, UndergroundPosition = new(4, 5), AccessPosition = new(4, 5) }],
Robot = new() { Position = new(10, 5) },
Reactors = [
new() {
@@ -1134,7 +1134,7 @@ public sealed partial class MainWindow
return layer switch {
EEditorLayer.Electricity => ECarrierType.Electricity,
EEditorLayer.Fuel => ECarrierType.Fuel,
EEditorLayer.Coolant => ECarrierType.Coolant,
EEditorLayer.Water => ECarrierType.Water,
_ => null
};
}
@@ -1217,7 +1217,7 @@ public sealed partial class MainWindow
{
return carrier switch {
ECarrierType.Fuel => c_FuelColor,
ECarrierType.Coolant => c_CoolantColor,
ECarrierType.Water => c_WaterColor,
ECarrierType.Electricity => c_ElectricityColor,
_ => Colors.White
};
@@ -1256,7 +1256,7 @@ public sealed partial class MainWindow
{
return carrier switch {
ECarrierType.Fuel => "F",
ECarrierType.Coolant => "C",
ECarrierType.Water => "C",
ECarrierType.Electricity => "E",
_ => "?"
};
@@ -1266,7 +1266,7 @@ public sealed partial class MainWindow
{
return remedy switch {
ERemedyType.FuelNeutralizer => "F REM",
ERemedyType.CoolantNeutralizer => "C REM",
ERemedyType.WaterNeutralizer => "C REM",
ERemedyType.ElectricityNeutralizer => "E REM",
ERemedyType.HeatShield => "H SHD",
_ => "REM"
@@ -1295,25 +1295,25 @@ public sealed partial class MainWindow
Tool(EEditorTool.Floor, "Floor"),
Tool(EEditorTool.Wall, "Wall"),
CarrierTool(EEditorTool.Underground, ECarrierType.Fuel, "Fuel Net"),
CarrierTool(EEditorTool.Underground, ECarrierType.Coolant, "Coolant Net"),
CarrierTool(EEditorTool.Underground, ECarrierType.Water, "Water Net"),
CarrierTool(EEditorTool.Underground, ECarrierType.Electricity, "Electric Net"),
CarrierTool(EEditorTool.Flow, ECarrierType.Fuel, "Fuel Source"),
CarrierTool(EEditorTool.Flow, ECarrierType.Coolant, "Coolant Source"),
CarrierTool(EEditorTool.Flow, ECarrierType.Water, "Water Source"),
CarrierTool(EEditorTool.Flow, ECarrierType.Electricity, "Electric Source"),
Tool(EEditorTool.Consumer, "Consumer"),
Tool(EEditorTool.Junction, "Junction"),
Tool(EEditorTool.Door, "Door"),
Tool(EEditorTool.AllSeeingEyeTerminal, "Eye Terminal"),
RemedyTool(ERemedyType.FuelNeutralizer, "Fuel Remedy"),
RemedyTool(ERemedyType.CoolantNeutralizer, "Coolant Remedy"),
RemedyTool(ERemedyType.WaterNeutralizer, "Water Remedy"),
RemedyTool(ERemedyType.ElectricityNeutralizer, "Electric Remedy"),
RemedyTool(ERemedyType.HeatShield, "Heat Shield"),
Tool(EEditorTool.ReactorControl, "Reactor"),
CarrierTool(EEditorTool.Leak, ECarrierType.Fuel, "Fuel Leak"),
CarrierTool(EEditorTool.Leak, ECarrierType.Coolant, "Coolant Leak"),
CarrierTool(EEditorTool.Leak, ECarrierType.Water, "Water Leak"),
CarrierTool(EEditorTool.Leak, ECarrierType.Electricity, "Electric Leak"),
CarrierTool(EEditorTool.SurfaceHazard, ECarrierType.Fuel, "Fuel Hazard"),
CarrierTool(EEditorTool.SurfaceHazard, ECarrierType.Coolant, "Coolant Hazard"),
CarrierTool(EEditorTool.SurfaceHazard, ECarrierType.Water, "Water Hazard"),
CarrierTool(EEditorTool.SurfaceHazard, ECarrierType.Electricity, "Electric Hazard"),
Tool(EEditorTool.Heat, "Heat"),
Tool(EEditorTool.Robot, "Robot")
@@ -1363,7 +1363,7 @@ public sealed partial class MainWindow
private const int c_BottomRightCorner = 8;
private const int c_AllCorners = c_TopLeftCorner | c_TopRightCorner | c_BottomLeftCorner | c_BottomRightCorner;
private static readonly Color c_FuelColor = ColorHelper.FromArgb(255, 220, 65, 65);
private static readonly Color c_CoolantColor = ColorHelper.FromArgb(255, 57, 174, 196);
private static readonly Color c_WaterColor = ColorHelper.FromArgb(255, 57, 174, 196);
private static readonly Color c_ElectricityColor = ColorHelper.FromArgb(255, 236, 226, 82);
private readonly IReadOnlyList<EditorToolViewModel> m_EditorTools = [];
private readonly EditorImageRegistry m_Images = new();

View File

@@ -32,13 +32,13 @@ public sealed class LevelEditorTests
var level = LevelState.Create("Wall editor", 6, 6);
var position = new GridPosition(2, 2);
level = LevelEditor.Apply(level, position, new() { Tool = EEditorTool.Underground, Carrier = ECarrierType.Fuel });
level = LevelEditor.Apply(level, position, new() { Tool = EEditorTool.Underground, Carrier = ECarrierType.Coolant });
level = LevelEditor.Apply(level, position, new() { Tool = EEditorTool.Underground, Carrier = ECarrierType.Water });
level = LevelEditor.Apply(level, position, new() { Tool = EEditorTool.Underground, Carrier = ECarrierType.Electricity });
var next = LevelEditor.Apply(level, position, new() { Tool = EEditorTool.Wall });
Assert.Equal(EUndergroundState.Intact, next.GetUnderground(position, ECarrierType.Fuel).State);
Assert.Equal(EUndergroundState.Intact, next.GetUnderground(position, ECarrierType.Coolant).State);
Assert.Equal(EUndergroundState.Intact, next.GetUnderground(position, ECarrierType.Water).State);
Assert.Equal(EUndergroundState.Intact, next.GetUnderground(position, ECarrierType.Electricity).State);
}
@@ -66,7 +66,7 @@ public sealed class LevelEditorTests
Assert.Equal(EPropType.Consumer, next.GetProp(new(2, 2)).Type);
Assert.Equal(EConsumerServiceState.Unknown, next.GetProp(new(2, 2)).FuelServiceState);
Assert.Equal(EConsumerServiceState.Unknown, next.GetProp(new(2, 2)).CoolantServiceState);
Assert.Equal(EConsumerServiceState.Unknown, next.GetProp(new(2, 2)).WaterServiceState);
Assert.Equal(EConsumerServiceState.Unknown, next.GetProp(new(2, 2)).ElectricityServiceState);
}

View File

@@ -11,7 +11,7 @@ public sealed class SimulationEngineTests
var consumer = next.GetProp(new(3, 3));
Assert.Equal(EConsumerServiceState.Producing, consumer.FuelServiceState);
Assert.Equal(EConsumerServiceState.Producing, consumer.CoolantServiceState);
Assert.Equal(EConsumerServiceState.Producing, consumer.WaterServiceState);
Assert.Equal(EConsumerServiceState.Producing, consumer.ElectricityServiceState);
Assert.Equal(ELevelState.Ready, next.Global.LevelState);
Assert.Contains(next.Forecasts, forecast => forecast.Kind == EForecastKind.ReactorReady);
@@ -52,7 +52,7 @@ public sealed class SimulationEngineTests
var consumer = next.GetProp(new(2, 2));
Assert.Equal(EConsumerServiceState.Disabled, consumer.FuelServiceState);
Assert.Equal(EConsumerServiceState.Unknown, consumer.CoolantServiceState);
Assert.Equal(EConsumerServiceState.Unknown, consumer.WaterServiceState);
Assert.Equal(EConsumerServiceState.Unknown, consumer.ElectricityServiceState);
Assert.Equal(EConsumerServiceState.Disabled, consumer.ServiceState);
}
@@ -277,12 +277,12 @@ public sealed class SimulationEngineTests
{
var level = LevelState.Create("Ready", 8, 7);
level = AddLine(level, ECarrierType.Fuel, new(2, 3), new(3, 3));
level = AddLine(level, ECarrierType.Coolant, new(2, 3), new(3, 3));
level = AddLine(level, ECarrierType.Water, new(2, 3), new(3, 3));
level = AddLine(level, ECarrierType.Electricity, new(2, 3), new(3, 3));
level = level.SetProp(new(2, 3), new() { Type = EPropType.Flow, Carrier = ECarrierType.Fuel });
level = level.SetProp(new(2, 2), new() { Type = EPropType.Flow, Carrier = ECarrierType.Coolant });
level = level.SetUnderground(new(2, 2), ECarrierType.Coolant, new() { State = EUndergroundState.Intact });
level = level.SetUnderground(new(2, 3), ECarrierType.Coolant, new() { State = EUndergroundState.Intact });
level = level.SetProp(new(2, 2), new() { Type = EPropType.Flow, Carrier = ECarrierType.Water });
level = level.SetUnderground(new(2, 2), ECarrierType.Water, new() { State = EUndergroundState.Intact });
level = level.SetUnderground(new(2, 3), ECarrierType.Water, new() { State = EUndergroundState.Intact });
level = level.SetProp(new(2, 4), new() { Type = EPropType.Flow, Carrier = ECarrierType.Electricity });
level = level.SetUnderground(new(2, 4), ECarrierType.Electricity, new() { State = EUndergroundState.Intact });
level = level.SetUnderground(new(2, 3), ECarrierType.Electricity, new() { State = EUndergroundState.Intact });