From 554ea117907bdb53f9f537672cbed04d6f3b539a Mon Sep 17 00:00:00 2001 From: RMROC451 Date: Sat, 30 Aug 2025 22:38:31 -0500 Subject: [PATCH] 2.1.5 updates passenger car load info in tags; refactor cog logic again; formatting updates for webhook messages --- TweaksAndThings/Commands/CrewUpdateCommand.cs | 14 +- ...ersHelper_SendAutoEngineerCommand_Patch.cs | 4 +- ...Controls_ConfigureOptionsDropdown_Patch.cs | 277 +++++++++++------- .../Patches/ExpandedConsole_Patch.cs | 86 ++++-- .../Patches/TagController_UpdateTag_Patch.cs | 16 + 5 files changed, 273 insertions(+), 124 deletions(-) diff --git a/TweaksAndThings/Commands/CrewUpdateCommand.cs b/TweaksAndThings/Commands/CrewUpdateCommand.cs index f2cffdc..b1bec51 100644 --- a/TweaksAndThings/Commands/CrewUpdateCommand.cs +++ b/TweaksAndThings/Commands/CrewUpdateCommand.cs @@ -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; } diff --git a/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs b/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs index d398bed..ec56c13 100644 --- a/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs +++ b/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs @@ -86,10 +86,10 @@ internal class AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch noCaboose(); logMessage += $"\nGovern AE? {output}"; - if (_log.IsEnabled(Serilog.Events.LogEventLevel.Debug)) + if (_log.IsEnabled(Serilog.Events.LogEventLevel.Verbose)) logMessage += $"\n{Environment.StackTrace}"; - _log.Information(logMessage); + _log.Debug(logMessage); return output; } diff --git a/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs b/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs index bb1d42b..f675d37 100644 --- a/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs +++ b/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs @@ -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(); - - private static readonly HashSet _keyChangeObservers = new(); - private static readonly Dictionary> locoConsistDestinations = new(); - private static string _lastSelectedLoco = string.Empty; - + private static readonly HashSet _keyChangeObservers = []; + private static HashSet 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.Shared; - if (!tweaksAndThings.IsEnabled() || (locoConsistDestinations.TryGetValue(getDictKey(selectedLoco), out Dictionary cars) && cars != null && _lastSelectedLoco == getDictKey(selectedLoco))) return; + try + { + PrepLocoUsage(__instance, out BaseLocomotive selectedLoco, out int numberOfCars); + TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; + if (!tweaksAndThings.IsEnabled() || !ShouldRecalc(selectedLoco)) return; - _lastSelectedLoco = getDictKey(selectedLoco); + timetableSaveTime = TimetableController.Shared.CurrentDocument.Modified; + Messenger.Default.Unregister(placeholder); - IterateCarsDetectDestinations( - __instance, - __result, - selectedLoco, - numberOfCars, - out List rowDatas, - out Action func, - out int origCount, - out int maxRowOrig, - out AutoEngineerOrdersHelper aeoh - ); + 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); + }); - List<(string destinationId, string destination, float? distance, Location? location)> jumpTos = BuildJumpToOptions(__instance, selectedLoco); + IterateCarsDetectDestinations( + __instance, + __result, + selectedLoco, + numberOfCars, + out List rowDatas, + out Action func, + out int origCount, + out int maxRowOrig, + out AutoEngineerOrdersHelper aeoh + ); - __result = WireUpJumpTosToSettingMenu( - __instance, - selectedLoco, - rowDatas, - func, - origCount, - maxRowOrig, - aeoh, - ref jumpTos - ); + 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 rowDatas, Action 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 consist = new List(); + consist = selectedLoco.EnumerateCoupled().ToList(); + HashSet 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 rowDatas, Action 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 list = new List(); + 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,54 +270,42 @@ 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 seen = new(); + locoConsistDestinations = []; + HashSet 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(); @@ -262,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); @@ -278,67 +369,53 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch _log.Debug($"{getDictKey(car)} OBSV {key}: {value}"); - if (locoConsistDestinations.TryGetValue(getDictKey(TrainController.Shared.SelectedLocomotive), out Dictionary cars) && cars == null) return; + 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 (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(); - if (locoConsistDestinations.TryGetValue(getDictKey(TrainController.Shared.SelectedLocomotive), out _)) locoConsistDestinations[getDictKey(TrainController.Shared.SelectedLocomotive)] = null; - 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; } } diff --git a/TweaksAndThings/Patches/ExpandedConsole_Patch.cs b/TweaksAndThings/Patches/ExpandedConsole_Patch.cs index 50b2461..19fa469 100644 --- a/TweaksAndThings/Patches/ExpandedConsole_Patch.cs +++ b/TweaksAndThings/Patches/ExpandedConsole_Patch.cs @@ -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(); 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 +■{loco}■ {msg} +```"; + + private static string west => @"```ansi +◀{loco}  {msg} +```"; + + private static string east => @"```ansi + {loco}▶ {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.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(); 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; diff --git a/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs b/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs index c349e2b..dc46cd5 100644 --- a/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs +++ b/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs @@ -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 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 {