Files
zfxaction26_2/src/ReactorMaintenance.Simulation/Systems/PlayerActionSystem.cs
2026-05-14 10:00:08 +02:00

133 lines
5.7 KiB
C#

namespace ReactorMaintenance.Simulation;
internal static class PlayerActionSystem
{
public static LevelState MoveRobot(LevelState level, GridPosition destination)
{
if (!CanAct(level) || !level.IsFloor(destination) || level.Robot.Position.ManhattanDistance(destination) != 1)
return Refuse(level, "MOVE BLOCKED");
return level with {
Robot = level.Robot with {
Position = destination,
HeatImmunitySteps = Math.Max(0, level.Robot.HeatImmunitySteps - 1)
}
};
}
public static LevelState InteractProp(LevelState level, Func<LevelState, LevelState> resolveLengthyAction)
{
if (!CanAct(level))
return Refuse(level, "NO CONTROL");
var position = level.Robot.Position;
var prop = level.GetProp(position);
if (prop.Type == EPropType.None)
return Refuse(level, "NO PROP");
var next = prop.Type switch {
EPropType.Flow or EPropType.Consumer => ToggleProp(level, position, prop),
EPropType.Junction => CycleJunctionMode(level, position, prop),
EPropType.Door => ToggleDoor(level, position, prop),
EPropType.AllSeeingEyeTerminal => level with { Global = level.Global with { Status = "ALL-SEEING-EYE AVAILABLE" } },
EPropType.RemedySupply => PickUpRemedy(level, position, prop),
EPropType.ReactorControl => ReactorSystem.Activate(level),
_ => level
};
return prop.Type == EPropType.AllSeeingEyeTerminal || prop.Type == EPropType.ReactorControl ? next : resolveLengthyAction(next);
}
public static LevelState InteractLeak(LevelState level, ECarrierType carrier, bool useRemedy, Func<LevelState, LevelState> resolveLengthyAction)
{
if (!CanAct(level))
return Refuse(level, "NO CONTROL");
var leakIndex = level.Leaks.ToList().FindIndex(leak => !leak.Repaired && leak.Carrier == carrier && leak.AccessPosition == level.Robot.Position);
if (leakIndex < 0)
return Refuse(level, "NO REACHABLE LEAK");
var leak = level.Leaks[leakIndex];
var next = useRemedy ? ApplyElementRemedy(level, leak) : RepairLeak(level, leakIndex, leak);
return resolveLengthyAction(next);
}
public static LevelState ApplyHeatShield(LevelState level, Func<LevelState, LevelState> resolveLengthyAction)
{
if (!CanAct(level) || level.Robot.HeatShields <= 0)
return Refuse(level, "NO HEAT SHIELD");
return resolveLengthyAction(level with {
Robot = level.Robot.Spend(ERemedyType.HeatShield) with { HeatImmunitySteps = Balancing.Current.HeatShieldSteps }
});
}
private static LevelState ToggleProp(LevelState level, GridPosition position, PropState prop)
{
var switchState = prop.SwitchState == EPropSwitchState.Enabled ? EPropSwitchState.Disabled : EPropSwitchState.Enabled;
return level.SetProp(position, prop with { SwitchState = switchState });
}
private static LevelState ToggleDoor(LevelState level, GridPosition position, PropState prop)
{
var doorState = prop.DoorState == EDoorState.Open ? EDoorState.Closed : EDoorState.Open;
return level.SetProp(position, prop with { DoorState = doorState });
}
private static LevelState PickUpRemedy(LevelState level, GridPosition position, PropState prop)
{
if (prop.Depleted || level.Robot.Count(prop.RemedyType) >= Balancing.Current.InventoryCapacityPerRemedy)
return level;
return level.SetProp(position, prop with { Depleted = true }) with { Robot = level.Robot.Add(prop.RemedyType, 1) };
}
private static LevelState RepairLeak(LevelState level, int leakIndex, LeakState leak)
{
var leaks = level.Leaks.ToArray();
leaks[leakIndex] = leak with { Repaired = true };
return level.SetUnderground(leak.UndergroundPosition, leak.Carrier, level.GetUnderground(leak.UndergroundPosition, leak.Carrier) with {
State = EUndergroundState.Intact,
StructuralIntegrity = Balancing.Current.MaxStructuralIntegrity
}) with {
Leaks = leaks
};
}
private static LevelState ApplyElementRemedy(LevelState level, LeakState leak)
{
var remedy = leak.Carrier switch {
ECarrierType.Fuel => ERemedyType.FuelNeutralizer,
ECarrierType.Water => ERemedyType.WaterNeutralizer,
ECarrierType.Electricity => ERemedyType.ElectricityNeutralizer,
_ => throw new ArgumentOutOfRangeException(nameof(leak), leak.Carrier, "Unsupported leak carrier.")
};
if (level.Robot.Count(remedy) <= 0)
return Refuse(level, "NO REMEDY");
var surface = SurfaceCarrierMath.RemoveCarrier(level.GetSurface(leak.AccessPosition), leak.Carrier);
return level.SetSurface(leak.AccessPosition, surface) with { Robot = level.Robot.Spend(remedy) };
}
private static LevelState CycleJunctionMode(LevelState level, GridPosition position, PropState prop)
{
var flow = JunctionFlowAnalyzer.Analyze(level).FirstOrDefault(junction => junction.Position == position);
var outflowCount = flow?.OutgoingBranches.Count ?? 2;
var ratios = Balancing.Current.JunctionRatios(outflowCount);
if (ratios.Count == 0)
return level;
return level.SetProp(position, prop with { JunctionMode = (prop.JunctionMode + 1) % ratios.Count });
}
private static bool CanAct(LevelState level)
{
return level.Global.LevelState is not (ELevelState.Lost or ELevelState.Won);
}
private static LevelState Refuse(LevelState level, string message)
{
return level with { Global = level.Global with { Status = message } };
}
}