mirror of
https://github.com/rmroc451/TweaksAndThings.git
synced 2025-12-16 09:19:37 -06:00
Compare commits
5 Commits
v2.1.3-bet
...
v2.1.5-bet
| Author | SHA1 | Date | |
|---|---|---|---|
| 554ea11790 | |||
| c114aec3b3 | |||
| 38a43276f7 | |||
| afd18b9d27 | |||
| 52e9fe1cab |
@@ -2,6 +2,6 @@
|
||||
<PropertyGroup>
|
||||
<MajorVersion>2</MajorVersion>
|
||||
<MinorVersion>1</MinorVersion>
|
||||
<PatchVersion>3</PatchVersion>
|
||||
<PatchVersion>4</PatchVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -2,6 +2,7 @@
|
||||
using Game.State;
|
||||
using Helpers;
|
||||
using Model.Ops;
|
||||
using Model.Ops.Timetable;
|
||||
using Network;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RMROC451.TweaksAndThings.Patches;
|
||||
@@ -46,9 +47,16 @@ public class EchoCommand : IConsoleCommand
|
||||
EntityReference loco = new EntityReference(EntityType.Car, car.id);
|
||||
if (comps[2] == "+") message = new Hyperlink(entityReference.URI(), string.Format(message, OpsController.Shared.ClosestArea(car)?.name ?? "???"));
|
||||
|
||||
if (StateManager.IsHost) car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}");
|
||||
ExpandedConsole_Add_Patch.SendMs(null, $"{Hyperlink.To(car)} {message}");
|
||||
if (!StateManager.IsHost) Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\"");
|
||||
string hlt = Hyperlink.To(car);
|
||||
hlt = car.TryGetTimetableTrain(out Timetable.Train t) ? hlt.Replace(car.DisplayName, t.DisplayStringLong) : hlt;
|
||||
|
||||
if (StateManager.IsHost)
|
||||
{
|
||||
car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}");
|
||||
|
||||
ExpandedConsole_Add_Patch.SendMs(null, $"{StateManager.Shared._playersManager.LocalPlayer} {hlt} {message}");
|
||||
}
|
||||
if (!StateManager.IsHost) Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {hlt}: \"{message}\"");
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public static class Car_Extensions
|
||||
FindMyCaboose(car, 0f, decrement: false, requireLoad: false);
|
||||
|
||||
public static Car? FindMyCabooseWithLoadRequirement(this Car car, float timeNeeded, bool decrement) =>
|
||||
FindMyCaboose(car, timeNeeded, decrement, requireLoad: false);
|
||||
FindMyCaboose(car, timeNeeded, decrement, requireLoad: true);
|
||||
|
||||
private static Car? FindMyCaboose(this Car car, float timeNeeded, bool decrement = false, bool requireLoad = true) =>
|
||||
(
|
||||
|
||||
@@ -86,8 +86,10 @@ internal class AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch
|
||||
noCaboose();
|
||||
|
||||
logMessage += $"\nGovern AE? {output}";
|
||||
if (_log.IsEnabled(Serilog.Events.LogEventLevel.Verbose))
|
||||
logMessage += $"\n{Environment.StackTrace}";
|
||||
|
||||
_log.Information(logMessage);
|
||||
_log.Debug(logMessage);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using Game.State;
|
||||
using GalaSoft.MvvmLight.Messaging;
|
||||
using Game.Events;
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using KeyValue.Runtime;
|
||||
using Model;
|
||||
using Model.AI;
|
||||
using Model.Ops;
|
||||
using Model.Ops.Timetable;
|
||||
using Model.Physics;
|
||||
using Network;
|
||||
using Network.Messages;
|
||||
@@ -27,51 +31,93 @@ namespace RMROC451.TweaksAndThings.Patches;
|
||||
internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
{
|
||||
private static Serilog.ILogger _log => Log.ForContext<AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch>();
|
||||
|
||||
private static readonly HashSet<IDisposable> _keyChangeObservers = new();
|
||||
private static readonly Dictionary<string, Dictionary<string, OpsCarPosition>> locoConsistDestinations = new();
|
||||
private static readonly HashSet<Car> consist = new();
|
||||
private static string _lastLocoCarId = string.Empty;
|
||||
private static bool recalcing = false;
|
||||
|
||||
private static readonly HashSet<IDisposable> _keyChangeObservers = [];
|
||||
private static HashSet<OpsCarPosition?> locoConsistDestinations = [];
|
||||
private static Game.GameDateTime? timetableSaveTime = null;
|
||||
static string getDictKey(Car car) => car.DisplayName;
|
||||
static Car placeholder = new();
|
||||
|
||||
static void Postfix(AutoEngineerWaypointControls __instance, ref OptionsDropdownConfiguration __result)
|
||||
{
|
||||
PrepLocoUsage(out BaseLocomotive selectedLoco, out int numberOfCars);
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled()) return;
|
||||
try
|
||||
{
|
||||
PrepLocoUsage(__instance, out BaseLocomotive selectedLoco, out int numberOfCars);
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled() || !ShouldRecalc(selectedLoco)) return;
|
||||
|
||||
IterateCarsDetectDestinations(
|
||||
__instance,
|
||||
__result,
|
||||
selectedLoco,
|
||||
numberOfCars,
|
||||
out List<DropdownMenu.RowData> rowDatas,
|
||||
out Action<int> func,
|
||||
out int origCount,
|
||||
out int maxRowOrig,
|
||||
out AutoEngineerOrdersHelper aeoh
|
||||
);
|
||||
timetableSaveTime = TimetableController.Shared.CurrentDocument.Modified;
|
||||
Messenger.Default.Unregister(placeholder);
|
||||
|
||||
List<(string destinationId, string destination, float? distance, Location? location)> jumpTos = BuildJumpToOptions(__instance, selectedLoco);
|
||||
Messenger.Default.Register(placeholder, delegate (TimetableDidChange evt)
|
||||
{
|
||||
_log.Debug("Received {evt}, rebuilding.", evt);
|
||||
Rebuild(__instance, selectedLoco, evt.GetType().Name);
|
||||
});
|
||||
Messenger.Default.Register(placeholder, delegate (CarTrainCrewChanged evt)
|
||||
{
|
||||
_log.Debug("Received {evt}, rebuilding {1} - {2}.", evt, selectedLoco.id, evt.CarId);
|
||||
Rebuild(__instance, selectedLoco, evt.GetType().Name);
|
||||
});
|
||||
Messenger.Default.Register(placeholder, delegate (TrainCrewsDidChange evt)
|
||||
{
|
||||
_log.Debug("Received {evt}, rebuilding.", evt);
|
||||
Rebuild(__instance, selectedLoco, evt.GetType().Name);
|
||||
});
|
||||
Messenger.Default.Register(placeholder, delegate (UpdateTrainCrews evt)
|
||||
{
|
||||
_log.Debug("Received {evt}, rebuilding.", evt);
|
||||
Rebuild(__instance, selectedLoco, evt.GetType().Name);
|
||||
});
|
||||
|
||||
__result = WireUpJumpTosToSettingMenu(
|
||||
__instance,
|
||||
selectedLoco,
|
||||
rowDatas,
|
||||
func,
|
||||
origCount,
|
||||
maxRowOrig,
|
||||
aeoh,
|
||||
ref jumpTos
|
||||
);
|
||||
IterateCarsDetectDestinations(
|
||||
__instance,
|
||||
__result,
|
||||
selectedLoco,
|
||||
numberOfCars,
|
||||
out List<DropdownMenu.RowData> rowDatas,
|
||||
out Action<int> func,
|
||||
out int origCount,
|
||||
out int maxRowOrig,
|
||||
out AutoEngineerOrdersHelper aeoh
|
||||
);
|
||||
|
||||
List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> jumpTos = BuildJumpToOptions(__instance, selectedLoco);
|
||||
|
||||
__result = WireUpJumpTosToSettingMenu(
|
||||
__instance,
|
||||
selectedLoco,
|
||||
rowDatas,
|
||||
func,
|
||||
origCount,
|
||||
maxRowOrig,
|
||||
aeoh,
|
||||
ref jumpTos
|
||||
);
|
||||
} catch(Exception ex)
|
||||
{
|
||||
_log.Error(ex, "I have a very unique set of skills; I will find you and I will squash you.");
|
||||
}
|
||||
}
|
||||
|
||||
private static OptionsDropdownConfiguration WireUpJumpTosToSettingMenu(AutoEngineerWaypointControls __instance, BaseLocomotive selectedLoco, List<DropdownMenu.RowData> rowDatas, Action<int> func, int origCount, int maxRowOrig, AutoEngineerOrdersHelper aeoh, ref List<(string destinationId, string destination, float? distance, Location? location)> jumpTos)
|
||||
private static bool ShouldRecalc(BaseLocomotive selectedLoco)
|
||||
{
|
||||
bool output = false;
|
||||
string locoKey = getDictKey(selectedLoco);
|
||||
List<Car> consist = new List<Car>();
|
||||
consist = selectedLoco.EnumerateCoupled().ToList();
|
||||
HashSet<OpsCarPosition?> destinations = consist.Where(c => GetCarDestinationIdentifier(c).HasValue).Select(GetCarDestinationIdentifier).ToHashSet();
|
||||
|
||||
output |= !locoConsistDestinations.Equals(destinations);
|
||||
output |= !(_keyChangeObservers?.Any() ?? false);
|
||||
output |= selectedLoco.TryGetTimetableTrain(out _) && TimetableController.Shared.CurrentDocument.Modified != timetableSaveTime;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static OptionsDropdownConfiguration WireUpJumpTosToSettingMenu(AutoEngineerWaypointControls __instance, BaseLocomotive selectedLoco, List<DropdownMenu.RowData> rowDatas, Action<int> func, int origCount, int maxRowOrig, AutoEngineerOrdersHelper aeoh, ref List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> jumpTos)
|
||||
{
|
||||
OptionsDropdownConfiguration __result;
|
||||
jumpTos = jumpTos?.OrderBy(c => c.distance ?? float.MaxValue)?.ToList() ?? [];
|
||||
jumpTos = jumpTos?.OrderBy(c => c.sortDistance)?.ToList() ?? default;
|
||||
var localJumpTos = jumpTos.ToList();
|
||||
var safetyFirst = AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.SafetyFirstGoverningApplies() && jumpTos.Any();
|
||||
|
||||
@@ -86,7 +132,7 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
rowDatas
|
||||
, delegate (int row)
|
||||
{
|
||||
_log.Debug($"{TrainController.Shared.SelectedLocomotive.DisplayName} row {row}/{localJumpTos.Count}/{rowDatas.Count}");
|
||||
_log.Debug($"{__instance.Locomotive.DisplayName} row {row}/{localJumpTos.Count}/{rowDatas.Count}");
|
||||
if (row <= maxRowOrig)
|
||||
{
|
||||
func(row);
|
||||
@@ -123,15 +169,15 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
return __result;
|
||||
}
|
||||
|
||||
private static List<(string destinationId, string destination, float? distance, Location? location)> BuildJumpToOptions(AutoEngineerWaypointControls __instance, BaseLocomotive selectedLoco)
|
||||
private static List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> BuildJumpToOptions(AutoEngineerWaypointControls __instance, BaseLocomotive selectedLoco)
|
||||
{
|
||||
List<(string destinationId, string destination, float? distance, Location? location)> jumpTos = new();
|
||||
foreach (var ocp in locoConsistDestinations[getDictKey(selectedLoco)].Values.Distinct())
|
||||
List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> jumpTos = new();
|
||||
foreach (OpsCarPosition ocp in locoConsistDestinations)
|
||||
{
|
||||
string destName = ocp.DisplayName;
|
||||
string destId = ocp.Identifier;
|
||||
float? distance = null;
|
||||
|
||||
float sortdistance = 0f;
|
||||
if (
|
||||
Graph.Shared.TryGetLocationFromPoint(
|
||||
ocp.Spans?.FirstOrDefault().GetSegments().FirstOrDefault(),
|
||||
@@ -149,16 +195,72 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
distance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum)
|
||||
? metrics.Distance
|
||||
: null;
|
||||
sortdistance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out metrics, checkForCars: false, 0f, trainMomentum)
|
||||
? metrics.Distance
|
||||
: float.MaxValue;
|
||||
};
|
||||
_log.Debug($"{getDictKey(selectedLoco)} -> {destName} {destId} {distance?.ToString()}");
|
||||
jumpTos.Add((
|
||||
destinationId: destId,
|
||||
destination: $"WP> {destName}"
|
||||
, distance: distance
|
||||
, sortdistance: sortdistance
|
||||
, location: (Location?)destLoc
|
||||
));
|
||||
}
|
||||
|
||||
if (selectedLoco.TryGetTimetableTrain(out Timetable.Train t))
|
||||
{
|
||||
_log.Information($"{getDictKey(selectedLoco)} -> {t.DisplayStringLong}");
|
||||
foreach (var e in t.Entries)
|
||||
{
|
||||
var stp = TimetableController.Shared.GetAllStations().FirstOrDefault(ps => ps.code == e.Station);
|
||||
_log.Information($"{getDictKey(selectedLoco)} -> {t.DisplayStringLong} -> {e.Station} {stp}");
|
||||
if (stp != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string destName = t.TrainType == Timetable.TrainType.Passenger ? stp.passengerStop.DisplayName : stp.DisplayName;
|
||||
string destId = t.TrainType == Timetable.TrainType.Passenger ? stp.passengerStop.identifier : stp.code;
|
||||
float? distance = null;
|
||||
float sortdistance = 0f;
|
||||
if (
|
||||
Graph.Shared.TryGetLocationFromPoint(
|
||||
stp.passengerStop.TrackSpans?.FirstOrDefault().GetSegments().FirstOrDefault(),
|
||||
stp.passengerStop.TrackSpans?.FirstOrDefault()?.GetCenterPoint() ?? default,
|
||||
200f,
|
||||
out Location destLoc
|
||||
)
|
||||
)
|
||||
{
|
||||
float trainMomentum = 0f;
|
||||
Location start = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.RouteStartLocation(out trainMomentum) : RouteStartLocation(__instance, selectedLoco);
|
||||
HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer;
|
||||
List<RouteSearch.Step> list = new List<RouteSearch.Step>();
|
||||
var totLen = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.CalculateTotalLength() : CalculateTotalLength(selectedLoco);
|
||||
distance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum)
|
||||
? metrics.Distance
|
||||
: null;
|
||||
sortdistance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out metrics, checkForCars: false, 0f, trainMomentum)
|
||||
? metrics.Distance
|
||||
: float.MaxValue;
|
||||
};
|
||||
_log.Debug($"{getDictKey(selectedLoco)} -> {destName} {destId} {distance?.ToString()}");
|
||||
jumpTos.Add((
|
||||
destinationId: destId,
|
||||
destination: $"{t.DisplayStringLong} > {destName}"
|
||||
, distance: distance
|
||||
, sortdistance: sortdistance
|
||||
, location: (Location?)destLoc
|
||||
));
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_log.Warning(ex, $"Timetable entry not added to AE gear cog options {stp}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jumpTos;
|
||||
}
|
||||
|
||||
@@ -168,59 +270,46 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
func = __result.OnRowSelected;
|
||||
origCount = rowDatas.Count;
|
||||
maxRowOrig = origCount - 1;
|
||||
if (!locoConsistDestinations.ContainsKey(getDictKey(selectedLoco))) locoConsistDestinations.Add(getDictKey(selectedLoco), new());
|
||||
|
||||
Dictionary<string, OpsCarPosition> seen = new();
|
||||
locoConsistDestinations = [];
|
||||
HashSet<OpsCarPosition?> seen = [];
|
||||
aeoh = new AutoEngineerOrdersHelper(persistence: new AutoEngineerPersistence(selectedLoco.KeyValueObject), locomotive: selectedLoco);
|
||||
Car.LogicalEnd logicalEnd = ((selectedLoco.set.IndexOfCar(selectedLoco).GetValueOrDefault(0) >= numberOfCars / 2) ? Car.LogicalEnd.B : Car.LogicalEnd.A);
|
||||
Car.LogicalEnd end = ((logicalEnd == Car.LogicalEnd.A) ? Car.LogicalEnd.B : Car.LogicalEnd.A);
|
||||
bool stop = false;
|
||||
int carIndex = selectedLoco.set.StartIndexForConnected(selectedLoco, logicalEnd, IntegrationSet.EnumerationCondition.Coupled);
|
||||
Car car;
|
||||
string locoKey = getDictKey(selectedLoco);
|
||||
while (!stop && (car = selectedLoco.set.NextCarConnected(ref carIndex, logicalEnd, IntegrationSet.EnumerationCondition.Coupled, out stop)) != null)
|
||||
{
|
||||
AddObserversToCar(__instance, car);
|
||||
var ocp = GetCarDestinationIdentifier(car);
|
||||
_log.Debug($"{getDictKey(selectedLoco)} --> {getDictKey(car)} -> {ocp.HasValue} {ocp?.DisplayName}");
|
||||
_log.Debug($"{locoKey} --> {getDictKey(car)} -> {ocp.HasValue} {ocp?.DisplayName}");
|
||||
|
||||
if (ocp.HasValue)
|
||||
{
|
||||
seen.Add(getDictKey(car), ocp.Value);
|
||||
if (locoConsistDestinations[getDictKey(selectedLoco)].TryGetValue(getDictKey(car), out _))
|
||||
locoConsistDestinations[getDictKey(selectedLoco)][getDictKey(car)] = ocp.Value;
|
||||
else
|
||||
locoConsistDestinations[getDictKey(selectedLoco)].Add(getDictKey(car), ocp.Value);
|
||||
seen.Add(ocp.Value);
|
||||
locoConsistDestinations.Add(ocp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
_log.Debug($"{getDictKey(selectedLoco)} --> [{seen.Keys.Count}] -> Seen -> {string.Join(Environment.NewLine, seen.Select(k => $"{k.Key}:{k.Value.DisplayName}"))}");
|
||||
_log.Debug($"{getDictKey(selectedLoco)} --> [{locoConsistDestinations[getDictKey(selectedLoco)].Keys.Count}] -> Cache -> {string.Join(Environment.NewLine, locoConsistDestinations[getDictKey(selectedLoco)].Select(k => $"{k.Key}:{k.Value.DisplayName}"))}");
|
||||
|
||||
var dropped =
|
||||
locoConsistDestinations[getDictKey(selectedLoco)].Keys.Except(seen.Keys).ToDictionary(
|
||||
t => t,
|
||||
t => locoConsistDestinations[getDictKey(selectedLoco)][t]
|
||||
); //are no longer here
|
||||
locoConsistDestinations.Except(seen).ToHashSet(); //are no longer here
|
||||
|
||||
_log.Debug($"{getDictKey(selectedLoco)} --> [{dropped.Keys.Count}] -> removed -> {string.Join(Environment.NewLine, dropped.Select(k => $"{k.Key}:{k.Value.DisplayName}"))}");
|
||||
|
||||
locoConsistDestinations[getDictKey(selectedLoco)] =
|
||||
locoConsistDestinations[getDictKey(selectedLoco)].Keys.Intersect(seen.Keys).ToDictionary(
|
||||
t => t,
|
||||
t => locoConsistDestinations[getDictKey(selectedLoco)][t]
|
||||
); //remove ones that are no longer here
|
||||
_log.Debug($"{locoKey} --> [{seen.Count}] -> Seen -> {string.Join(Environment.NewLine, seen.Select(k => k.Value.DisplayName))}");
|
||||
_log.Debug($"{locoKey} --> [{locoConsistDestinations.Count}] -> Cache -> {string.Join(Environment.NewLine, locoConsistDestinations.Select(k => $"{locoKey}:{k.Value.DisplayName}"))}");
|
||||
_log.Debug($"{locoKey} --> [{dropped.Count}] -> removed -> {string.Join(Environment.NewLine, dropped.Select(k => k.Value.DisplayName))}");
|
||||
locoConsistDestinations = locoConsistDestinations.Intersect(seen).ToHashSet(); //remove ones that are no longer here
|
||||
seen.Clear();
|
||||
}
|
||||
|
||||
private static void PrepLocoUsage(out BaseLocomotive selectedLoco, out int numberOfCars)
|
||||
private static void PrepLocoUsage(AutoEngineerWaypointControls __instance, out BaseLocomotive selectedLoco, out int numberOfCars)
|
||||
{
|
||||
//wire up that loco
|
||||
selectedLoco = TrainController.Shared.SelectedLocomotive;
|
||||
selectedLoco = __instance.Locomotive;
|
||||
numberOfCars = selectedLoco.set.NumberOfCars;
|
||||
_log.Debug($"{getDictKey(selectedLoco)} --> HI BOB[{numberOfCars}]");
|
||||
foreach (var o in _keyChangeObservers) o.Dispose();
|
||||
_keyChangeObservers.Clear();
|
||||
recalcing = false;
|
||||
}
|
||||
|
||||
private static OpsCarPosition? GetCarDestinationIdentifier(Car c)
|
||||
@@ -263,6 +352,7 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
{
|
||||
AddObserver(__instance, c, Car.KeyOpsWaybill);
|
||||
AddObserver(__instance, c, Car.KeyOpsRepairDestination);
|
||||
AddObserver(__instance, c, nameof(Car.trainCrewId));
|
||||
foreach (Car.LogicalEnd logicalEnd in CarInspector_PopulateCarPanel_Patch.ends)
|
||||
{
|
||||
AddObserver(__instance, c, Car.KeyValueKeyFor(Car.EndGearStateKey.IsCoupled, c.LogicalToEnd(logicalEnd)), true);
|
||||
@@ -276,67 +366,56 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
key,
|
||||
delegate (Value value)
|
||||
{
|
||||
if (recalcing) return;
|
||||
|
||||
_log.Debug($"{getDictKey(car)} OBSV {key}: {value}");
|
||||
|
||||
if (!(locoConsistDestinations?.Any() ?? false)) return;
|
||||
|
||||
bool waybillChng = (new[] { Car.KeyOpsWaybill, Car.KeyOpsRepairDestination }).Contains(key);
|
||||
string? destId =
|
||||
waybillChng ?
|
||||
GetCarDestinationIdentifier(car)?.Identifier ?? null :
|
||||
string? destId =
|
||||
waybillChng ?
|
||||
GetCarDestinationIdentifier(car)?.Identifier ?? null :
|
||||
null;
|
||||
if (waybillChng)
|
||||
{
|
||||
if (locoConsistDestinations.TryGetValue(getDictKey(TrainController.Shared.SelectedLocomotive), out Dictionary<string, OpsCarPosition> cars)
|
||||
&& cars.TryGetValue(getDictKey(car), out OpsCarPosition pos)
|
||||
&& pos.Identifier == destId)
|
||||
if (locoConsistDestinations.Contains(GetCarDestinationIdentifier(car)))
|
||||
{
|
||||
return;
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Debug($"{getDictKey(car)} OBSV {key}: destNew; {destId}; reload");
|
||||
}
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
_log.Debug($"{getDictKey(car)} OBSV {key}: {value}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach(var o in _keyChangeObservers)
|
||||
{
|
||||
o.Dispose();
|
||||
}
|
||||
var loco = TrainController.Shared.SelectedLocomotive;
|
||||
if (!TrainController.Shared.SelectRecall()) TrainController.Shared.SelectedCar = null;
|
||||
_keyChangeObservers.Clear();
|
||||
recalcing = true;
|
||||
TrainController.Shared.SelectedCar = loco;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
|
||||
}
|
||||
Rebuild(__instance, car, key);
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static Waybill? GetWaybill(Car car, Value waybillValue)
|
||||
private static void Rebuild(AutoEngineerWaypointControls __instance, Car car, string key)
|
||||
{
|
||||
Waybill? _waybill = null;
|
||||
try
|
||||
{
|
||||
_waybill = Model.Ops.Waybill.FromPropertyValue(waybillValue, OpsController.Shared);
|
||||
foreach (var o in _keyChangeObservers)
|
||||
{
|
||||
o.Dispose();
|
||||
}
|
||||
var loco = __instance.Locomotive;
|
||||
if (!TrainController.Shared.SelectRecall()) TrainController.Shared.SelectedCar = null;
|
||||
_keyChangeObservers.Clear();
|
||||
locoConsistDestinations = [];
|
||||
TrainController.Shared.SelectedCar = loco;
|
||||
}
|
||||
catch (OpsController.InvalidOpsCarPositionException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Waybill for car {car} contains an invalid ops position: {pos}", car, ex.Identifier);
|
||||
_waybill = null;
|
||||
_log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.Warning(exception, "{car} Exception in Waybill.FromPropertyValue", car);
|
||||
_waybill = null;
|
||||
}
|
||||
return _waybill;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Game;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using Helpers;
|
||||
using Model;
|
||||
using Model.Ops.Timetable;
|
||||
using Newtonsoft.Json;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
@@ -21,6 +22,7 @@ namespace RMROC451.TweaksAndThings.Patches;
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class ExpandedConsole_Add_Patch
|
||||
{
|
||||
private static Serilog.ILogger _log => Log.ForContext<ExpandedConsole_Add_Patch>();
|
||||
private static void Prefix(ref UI.Console.Console.Entry entry)
|
||||
{
|
||||
entry.Text = $"{entry.Timestamp} : {entry.Text}";
|
||||
@@ -28,12 +30,26 @@ internal class ExpandedConsole_Add_Patch
|
||||
SendMs((UI.Console.Console.Entry?)entry);
|
||||
}
|
||||
|
||||
|
||||
private static string hold => @"```ansi
|
||||
[2;40m[0m[2;40m[2;34m■[0m[2;40m[0m[2;34m[2;40m[0m[2;34m[0m[2;40m[1;2m[1;34m{loco}[0m[1;40m[0m[2;40m[0m[2;40m[1;2m[1;34m[0m[1;40m[0m[2;40m[0m[2;40m[2;34m■[0m[2;40m[0m[2;34m[2;40m[0m[2;34m[0m {msg}
|
||||
```";
|
||||
|
||||
private static string west => @"```ansi
|
||||
[2;40m[0m[2;31m[2;40m◀{loco}[0m[2;31m[0m[2;40m[2;31m [0m[2;40m[0m[2;40m[0m {msg}
|
||||
```";
|
||||
|
||||
private static string east => @"```ansi
|
||||
[2;40m[0m[2;32m[0m[2;40m [0m[2;36m[0m[2;40m[0m[2;36m[2;40m{loco}▶[0m[2;36m[0m {msg}
|
||||
```";
|
||||
|
||||
internal static void SendMs(UI.Console.Console.Entry? entry, string? text = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (entry is null && !String.IsNullOrEmpty(text)) entry = new() { Text = text };
|
||||
var msgText = entry?.Text ?? string.Empty;
|
||||
if (msgText.StartsWith("Usage:")) return;
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
StateManager shared = StateManager.Shared;
|
||||
GameStorage gameStorage = shared.Storage;
|
||||
@@ -50,31 +66,45 @@ internal class ExpandedConsole_Add_Patch
|
||||
|
||||
var carId = t.IsMatch(msgText) ? Regex.Match(msgText, "car:(.*?)\"").Groups[1].Captures[0].ToString() : string.Empty;
|
||||
Model.Car? car = TrainController.Shared.CarForString(carId);
|
||||
var data = UpdateCarText(car);
|
||||
bool engineInMessage = car?.IsLocomotive ?? false;
|
||||
var image = engineInMessage ?
|
||||
new
|
||||
{
|
||||
url = string.Empty
|
||||
} :
|
||||
null;
|
||||
string msgToSend = string.Empty;
|
||||
string desc = Regex.Replace(msgText, "<.*?>", "");
|
||||
desc = !!car ? desc.Replace(car?.DisplayName ?? string.Empty, string.Empty) : desc;
|
||||
desc = desc.Trim();//.Replace(": ", "\n");
|
||||
|
||||
if (engineInMessage)
|
||||
if (!!car && engineInMessage)
|
||||
{
|
||||
CTCPanelMarkerManager cTCPanelMarkerManager = UnityEngine.Object.FindObjectOfType<CTCPanelMarkerManager>();
|
||||
|
||||
CTCPanelMarker marker = cTCPanelMarkerManager?._markers?.Values?.FirstOrDefault(v => v.TooltipInfo.Text.Contains(car.Ident.RoadNumber));
|
||||
|
||||
string color = CTCPanelMarker.InferColorFromText(car?.DisplayName).HexString().Replace("#", string.Empty);
|
||||
if (marker != null)
|
||||
|
||||
string markerText = marker?.TooltipInfo.Text ?? string.Empty;
|
||||
if (markerText.StartsWith(">") || markerText.EndsWith(">") || data.Item2 == Timetable.Direction.East || msgText.Contains("*-"))
|
||||
{
|
||||
color = CTCPanelMarker.InferColorFromText(marker.TooltipInfo.Text).HexString().Replace("#", string.Empty);
|
||||
msgToSend = east.Replace("{loco}", car?.DisplayName.Replace(" ", string.Empty)).Replace("{msg}", desc);
|
||||
}
|
||||
image = new
|
||||
else if (markerText.StartsWith("<") || markerText.EndsWith("<") || data.Item2 == Timetable.Direction.West || msgText.Contains("-*"))
|
||||
{
|
||||
url = $"https://img.shields.io/badge/{car.DisplayName.Replace(" ", "%20")}-%20-{color}.png"
|
||||
};
|
||||
|
||||
msgToSend = west.Replace("{loco}", car?.DisplayName.Replace(" ", string.Empty)).Replace("{msg}", desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgToSend = hold.Replace("{loco}", car?.DisplayName.Replace(" ", string.Empty)).Replace("{msg}", desc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msgToSend = desc;
|
||||
}
|
||||
msgToSend = msgToSend
|
||||
.Replace(" , ", ", ")
|
||||
.Replace(", :", " :")
|
||||
.Replace(" ; ", "; ")
|
||||
.Replace("*-", string.Empty)
|
||||
.Replace("#", string.Empty)
|
||||
.Replace("-*", string.Empty)
|
||||
.Trim();
|
||||
|
||||
var SuccessWebHook = new
|
||||
{
|
||||
@@ -83,9 +113,7 @@ internal class ExpandedConsole_Add_Patch
|
||||
{
|
||||
new
|
||||
{
|
||||
description= Regex.Replace(msgText, "<.*?>", "").Replace(": ", "\n"),
|
||||
timestamp=DateTime.UtcNow,
|
||||
image
|
||||
description= msgToSend
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -103,6 +131,26 @@ internal class ExpandedConsole_Add_Patch
|
||||
}
|
||||
}
|
||||
|
||||
public static (string , Timetable.Direction?) UpdateCarText(Car car)
|
||||
{
|
||||
string output = string.Empty;
|
||||
Timetable.Direction? dir = null;
|
||||
if (car?.IsLocomotive ?? false)
|
||||
{
|
||||
if (StateManager.Shared.PlayersManager.TrainCrewForId(car.trainCrewId, out var trainCrew))
|
||||
{
|
||||
output = trainCrew.Name;
|
||||
if (TimetableController.Shared.TryGetTrainForTrainCrew(trainCrew, out Timetable.Train timetableTrain))
|
||||
{
|
||||
dir = timetableTrain.Direction;
|
||||
output += " (Train " + timetableTrain.DisplayStringShort + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
return (output, dir);
|
||||
}
|
||||
|
||||
|
||||
public static GameDateTime RealNow()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
@@ -48,8 +48,7 @@ internal class InterchangedIndustryLoader_ServiceInterchange_Patch
|
||||
if (num4 > 0)
|
||||
{
|
||||
var canAfford = shared.CanAfford(num4);
|
||||
penalty = Mathf.CeilToInt(!canAfford ? num4 * 0.2f : 0);
|
||||
__instance.Industry.ApplyToBalance(-num4, __instance.ledgerCategory, null, 0, quiet: true);
|
||||
penalty += Mathf.CeilToInt(!canAfford ? num4 * 0.2f : 0);
|
||||
|
||||
num2 += num4;
|
||||
num3++;
|
||||
|
||||
@@ -98,6 +98,7 @@ internal class OpsController_AnnounceCoalescedPayments_Patch
|
||||
var data = car.QuantityCapacityOfLoad(CrewHoursLoad());
|
||||
if ((data.quantity + quantityToLoad > data.capacity) && data.quantity < data.capacity)
|
||||
{
|
||||
quantityToLoad = data.capacity; //ensure topping off
|
||||
Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Caboose crew topped off.\"");
|
||||
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, false);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using KeyValue.Runtime;
|
||||
using Model;
|
||||
using Model.Ops;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RollingStock;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UI.Tags;
|
||||
using static Unity.IO.LowLevel.Unsafe.AsyncReadManagerMetrics;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
@@ -54,6 +58,18 @@ internal class TagController_UpdateTag_Patch
|
||||
if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
|
||||
if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
|
||||
|
||||
if (car.IsPassengerCar())
|
||||
{
|
||||
PassengerMarker? passengerMarker = car.GetPassengerMarker();
|
||||
if (passengerMarker.HasValue)
|
||||
{
|
||||
IEnumerable<string> loadInfo = car.PassengerCountString(passengerMarker).Split('/');
|
||||
//string item4 = CarPickable.PassengerString(car, passengerMarker.Value);
|
||||
string val = TextSprites.PiePercent(float.Parse(loadInfo.First()), float.Parse(loadInfo.Last())) + $" {car.PassengerCountString(passengerMarker)} Passengers";
|
||||
tagCallout.callout.Text = tagCallout.callout.Text.Contains("Empty") ? tagCallout.callout.Text.Replace("Empty", val) : tagCallout.callout.Text + $"\n{val}";
|
||||
}
|
||||
}
|
||||
|
||||
tagCallout.callout.Title =
|
||||
tags.Any() switch
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user