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 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 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 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 } }; } }