133 lines
5.7 KiB
C#
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 } };
|
|
}
|
|
} |