Adds end-gear couple/air/cut on the vanilla pie, plus a consist wheel for set-lead, brakes, bleed, air, and idle without opening extra windows.
275 lines
8.2 KiB
C#
275 lines
8.2 KiB
C#
using System.Collections.Generic;
|
|
using Game.Messages;
|
|
using Game.State;
|
|
using Model;
|
|
using S3.Core;
|
|
|
|
namespace S3.Modules.QuickActions;
|
|
|
|
static class EndGearActions
|
|
{
|
|
public static bool TryNeighbor(Car car, Car.LogicalEnd end, out Car other, out Car.LogicalEnd otherEnd)
|
|
{
|
|
otherEnd = end == Car.LogicalEnd.A ? Car.LogicalEnd.B : Car.LogicalEnd.A;
|
|
other = null!;
|
|
try
|
|
{
|
|
return car.TryGetAdjacentCar(end, out other) && other != null;
|
|
}
|
|
catch
|
|
{
|
|
other = null!;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static bool CanDisconnectAll(Car car, Car.LogicalEnd end)
|
|
{
|
|
return car[end].IsCoupled;
|
|
}
|
|
|
|
public static bool CanToggleCouple(Car car, Car.LogicalEnd end)
|
|
{
|
|
if (car[end].IsCoupled) return true;
|
|
return TryNeighbor(car, end, out _, out _);
|
|
}
|
|
|
|
public static bool CanToggleAir(Car car, Car.LogicalEnd end)
|
|
{
|
|
if (car[end].IsAirConnected) return true;
|
|
return car[end].IsCoupled && TryNeighbor(car, end, out _, out _);
|
|
}
|
|
|
|
public static void ToggleCouple(Car car, Car.LogicalEnd end)
|
|
{
|
|
bool couple = !car[end].IsCoupled;
|
|
bool hasNeighbor = TryNeighbor(car, end, out Car other, out Car.LogicalEnd otherEnd);
|
|
if (couple && !hasNeighbor) return;
|
|
|
|
try
|
|
{
|
|
car.ApplyEndGearChange(end, Car.EndGearStateKey.IsCoupled, couple);
|
|
if (hasNeighbor)
|
|
other.ApplyEndGearChange(otherEnd, Car.EndGearStateKey.IsCoupled, couple);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] couple toggle failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
public static void ToggleAir(Car car, Car.LogicalEnd end)
|
|
{
|
|
bool connect = !car[end].IsAirConnected;
|
|
if (connect && !car[end].IsCoupled) return;
|
|
|
|
Car? other = null;
|
|
try { other = car.CoupledTo(end) ?? car.AirConnectedTo(end); }
|
|
catch { /* fall through */ }
|
|
if (other == null && !TryNeighbor(car, end, out other, out _))
|
|
return;
|
|
if (other == null) return;
|
|
|
|
try
|
|
{
|
|
var msg = new SetGladhandsConnected(car.id, other.id, connect);
|
|
if (!StateManager.CheckAuthorizedToSendMessage(msg)) return;
|
|
StateManager.ApplyLocal(msg);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] air toggle failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
public static void ToggleCock(Car car, Car.LogicalEnd end)
|
|
{
|
|
float next = car[end].IsAnglecockOpen ? 0f : 1f;
|
|
try
|
|
{
|
|
car.ApplyEndGearChange(end, Car.EndGearStateKey.Anglecock, next);
|
|
if (car[end].IsCoupled && TryNeighbor(car, end, out Car other, out Car.LogicalEnd otherEnd))
|
|
other.ApplyEndGearChange(otherEnd, Car.EndGearStateKey.Anglecock, next);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] anglecock toggle failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
public static string Preview(Car car, Car.LogicalEnd end, HoverSlot slot)
|
|
{
|
|
return slot switch
|
|
{
|
|
HoverSlot.Cut => CutPreview(CountCut(car, end)),
|
|
_ => "",
|
|
};
|
|
}
|
|
|
|
static string CutPreview(int n)
|
|
{
|
|
if (n <= 0) return "";
|
|
return $"Cut out {n} Cars";
|
|
}
|
|
|
|
public static int CountCut(Car car, Car.LogicalEnd end)
|
|
{
|
|
if (!TryNeighbor(car, end, out Car other, out _))
|
|
return 0;
|
|
return CollectAway(other, car).Count;
|
|
}
|
|
|
|
public static void DisconnectAll(Car car, Car.LogicalEnd end)
|
|
{
|
|
if (!TryNeighbor(car, end, out Car other, out Car.LogicalEnd otherEnd))
|
|
return;
|
|
|
|
List<Car> detached = CollectAway(other, car);
|
|
var s = QuickActionsModule.Settings;
|
|
|
|
try
|
|
{
|
|
car.ApplyEndGearChange(end, Car.EndGearStateKey.Anglecock, 0f);
|
|
other.ApplyEndGearChange(otherEnd, Car.EndGearStateKey.Anglecock, 0f);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] drop-all anglecock failed: {e.Message}");
|
|
}
|
|
|
|
if (car[end].IsAirConnected)
|
|
{
|
|
try
|
|
{
|
|
var msg = new SetGladhandsConnected(car.id, other.id, false);
|
|
if (StateManager.CheckAuthorizedToSendMessage(msg))
|
|
StateManager.ApplyLocal(msg);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] drop-all air failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
car.ApplyEndGearChange(end, Car.EndGearStateKey.IsCoupled, false);
|
|
other.ApplyEndGearChange(otherEnd, Car.EndGearStateKey.IsCoupled, false);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] drop-all couple failed: {e.Message}");
|
|
}
|
|
|
|
if (!s.dropHandbrakeOnCut) return;
|
|
foreach (Car c in detached)
|
|
{
|
|
if (s.dropHandbrakeExcludeLocos && c is BaseLocomotive) continue;
|
|
try { c.SetHandbrake(true); }
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] drop-all handbrake {c.DisplayName}: {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool IsMadeUp(Car car, Car.LogicalEnd end)
|
|
{
|
|
try
|
|
{
|
|
var g = car[end];
|
|
return g.IsCoupled && g.IsAirConnected && g.IsAnglecockOpen;
|
|
}
|
|
catch { return false; }
|
|
}
|
|
|
|
public static bool CanToggleJoint(Car car, Car.LogicalEnd end)
|
|
{
|
|
try
|
|
{
|
|
if (car[end].IsCoupled) return true;
|
|
return TryNeighbor(car, end, out _, out _);
|
|
}
|
|
catch { return false; }
|
|
}
|
|
|
|
public static void ToggleJoint(Car car, Car.LogicalEnd end)
|
|
{
|
|
if (IsMadeUp(car, end)) DisconnectAll(car, end);
|
|
else MakeUp(car, end);
|
|
}
|
|
|
|
public static void MakeUp(Car car, Car.LogicalEnd end)
|
|
{
|
|
bool hasNeighbor = TryNeighbor(car, end, out Car other, out Car.LogicalEnd otherEnd);
|
|
if (!hasNeighbor)
|
|
{
|
|
try { if (!car[end].IsCoupled) return; }
|
|
catch { return; }
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!car[end].IsCoupled)
|
|
{
|
|
car.ApplyEndGearChange(end, Car.EndGearStateKey.IsCoupled, true);
|
|
if (hasNeighbor)
|
|
other.ApplyEndGearChange(otherEnd, Car.EndGearStateKey.IsCoupled, true);
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] make-up couple failed: {e.Message}");
|
|
}
|
|
|
|
Car? airOther = null;
|
|
try { airOther = car.CoupledTo(end) ?? other; }
|
|
catch { airOther = other; }
|
|
if (airOther != null)
|
|
{
|
|
try
|
|
{
|
|
if (!car[end].IsAirConnected)
|
|
{
|
|
var msg = new SetGladhandsConnected(car.id, airOther.id, true);
|
|
if (StateManager.CheckAuthorizedToSendMessage(msg))
|
|
StateManager.ApplyLocal(msg);
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] make-up air failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
car.ApplyEndGearChange(end, Car.EndGearStateKey.Anglecock, 1f);
|
|
if (hasNeighbor)
|
|
other.ApplyEndGearChange(otherEnd, Car.EndGearStateKey.Anglecock, 1f);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log.Warn($"[quickactions] make-up Anglecock failed: {e.Message}");
|
|
}
|
|
}
|
|
|
|
static List<Car> CollectAway(Car start, Car blocked)
|
|
{
|
|
var list = new List<Car>();
|
|
var seen = new HashSet<Car> { blocked };
|
|
var stack = new Stack<Car>();
|
|
stack.Push(start);
|
|
while (stack.Count > 0)
|
|
{
|
|
Car c = stack.Pop();
|
|
if (!seen.Add(c)) continue;
|
|
list.Add(c);
|
|
if (TryNeighbor(c, Car.LogicalEnd.A, out Car a, out _) && !seen.Contains(a))
|
|
stack.Push(a);
|
|
if (TryNeighbor(c, Car.LogicalEnd.B, out Car b, out _) && !seen.Contains(b))
|
|
stack.Push(b);
|
|
}
|
|
return list;
|
|
}
|
|
}
|