From 5774c7f04c9d8a806914ea644910db59cc6bd358 Mon Sep 17 00:00:00 2001 From: RMROC451 Date: Fri, 26 Jul 2024 12:25:03 -0500 Subject: [PATCH] #29 #30 Allow caboose setting to require that a caboose is present in the consist to use the AI Engineer's AutoOiler & AutoHotboxSpotter. --- Directory.Build.targets | 3 +- .../Extensions/AutoEngineer_Extensions.cs | 33 ++++-- .../AutoHotboxSpotter_SpotterLoop_Patch.cs | 5 +- .../Patches/AutoOiler_Loop_Patch.cs | 5 +- ...CarPickable_HandleShowContextMenu_Patch.cs | 10 +- TweaksAndThings/Settings/Settings.cs | 24 +++- TweaksAndThings/TweaksAndThingsPlugin.cs | 107 +++++++++++------- updates.json | 12 ++ 8 files changed, 135 insertions(+), 64 deletions(-) create mode 100644 updates.json diff --git a/Directory.Build.targets b/Directory.Build.targets index c0a2328..a8b529e 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -6,6 +6,7 @@ $(GameDir)/Mods/$(AssemblyName) $(GameModDir)/ + $([System.DateTime]::UtcNow.ToString(`o`)) @@ -30,7 +31,7 @@ - + $(OutputPath)/Mods diff --git a/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs b/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs index 9de603e..ddfc50f 100644 --- a/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs +++ b/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs @@ -12,7 +12,7 @@ namespace RMROC451.TweaksAndThings.Extensions private static float CabooseAutoOilerLimit(this bool hasCaboose) => hasCaboose ? 0.99f : AutoOiler.OilIfBelow; - public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log) + public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log, bool cabooseRequired) { int originIndex = oiler.FindOriginIndex(); bool hasCaboose = oiler._cars.CabooseInConsist(); @@ -21,13 +21,17 @@ namespace RMROC451.TweaksAndThings.Extensions _log.Error("Couldn't find origin car {car}", oiler._originCar); oiler._coroutine = null; yield break; + } else if (CabooseRequirementChecker(string.Format("{0} {1}", oiler.GetType().Name, oiler.name), cabooseRequired, hasCaboose, _log)) + { + yield break; } oiler._reverse = originIndex > oiler._cars.Count - originIndex; _log.Information( - "AutoOiler {name} starting, rev = {reverse}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}", - oiler.name, - oiler._reverse, - hasCaboose, + "AutoOiler {name} starting, rev = {reverse}, caboose required = {req}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}", + oiler.name, + oiler._reverse, + cabooseRequired, + hasCaboose, hasCaboose.CabooseAutoOilerLimit() ); while (true) @@ -63,7 +67,7 @@ namespace RMROC451.TweaksAndThings.Extensions } } - public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log) + public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log, bool cabooseRequired) { while (true) { @@ -73,7 +77,11 @@ namespace RMROC451.TweaksAndThings.Extensions yield return new WaitForSeconds(1f); continue; } - _log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Has Caboose => {hasCaboose}; Has Cars {hasCars}", spotter.name, hasCaboose, spotter.HasCars); + _log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Has Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}", spotter.name, hasCaboose, spotter.HasCars, cabooseRequired); + if (CabooseRequirementChecker(string.Format("{0} {1}", spotter.GetType().Name, spotter.name), cabooseRequired, hasCaboose, _log)) + { + yield break; + } spotter.CheckForHotbox(); while (spotter.HasCars) { @@ -82,12 +90,21 @@ namespace RMROC451.TweaksAndThings.Extensions { var numOrig = num; num = Random.Range(15, 30); - _log.Information("AutoHotboxSpotter {name}: Next check went from num(60,300) => {numOrig}; to num(15,30) => {hasCaboose}", spotter.name, numOrig, num); + _log.Information("AutoHotboxSpotter {name}: Next check went from num(60,300) => {numOrig}; to num(15,30) => {hasCaboose}; Requires Caboose {requiresCaboose}", spotter.name, numOrig, num, hasCaboose, cabooseRequired); } yield return new WaitForSeconds(num); spotter.CheckForHotbox(); } } } + + private static bool CabooseRequirementChecker(string name, bool cabooseRequired, bool hasCaboose, Serilog.ILogger _log) + { + bool error = cabooseRequired && !hasCaboose; + if (error) { + _log.Debug("{name}: Couldn't find required caboose!", name); + } + return error; + } } } diff --git a/TweaksAndThings/Patches/AutoHotboxSpotter_SpotterLoop_Patch.cs b/TweaksAndThings/Patches/AutoHotboxSpotter_SpotterLoop_Patch.cs index 6b742ce..f0fd551 100644 --- a/TweaksAndThings/Patches/AutoHotboxSpotter_SpotterLoop_Patch.cs +++ b/TweaksAndThings/Patches/AutoHotboxSpotter_SpotterLoop_Patch.cs @@ -18,9 +18,10 @@ internal class AutoHotboxSpotter_SpotterLoop_Patch { TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; if (!tweaksAndThings.IsEnabled) return true; - bool buttonsHaveCost = tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false; + bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment(); + bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter(); - if (buttonsHaveCost) __result = __instance.MrocAutoHotboxSpotterLoop(_log); + if (buttonsHaveCost) __result = __instance.MrocAutoHotboxSpotterLoop(_log, cabooseRequired); return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine } } diff --git a/TweaksAndThings/Patches/AutoOiler_Loop_Patch.cs b/TweaksAndThings/Patches/AutoOiler_Loop_Patch.cs index c496de8..afc2602 100644 --- a/TweaksAndThings/Patches/AutoOiler_Loop_Patch.cs +++ b/TweaksAndThings/Patches/AutoOiler_Loop_Patch.cs @@ -18,9 +18,10 @@ internal class AutoOiler_Loop_Patch { TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; if (!tweaksAndThings.IsEnabled) return true; - bool buttonsHaveCost = tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false; + bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment(); + bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter(); - if (buttonsHaveCost)__result = __instance.MrocAutoOilerLoop(_log); + if (buttonsHaveCost) __result = __instance.MrocAutoOilerLoop(_log, cabooseRequired); return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine } } diff --git a/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs b/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs index 36e82c9..6e8b31c 100644 --- a/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs +++ b/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs @@ -7,7 +7,6 @@ using RollingStock; using System.Linq; using UI; using UI.ContextMenu; -using static Model.Car; namespace RMROC451.TweaksAndThings.Patches; @@ -21,15 +20,14 @@ internal class CarPickable_HandleShowContextMenu_Patch TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; if (!tweaksAndThings.IsEnabled) return; - bool buttonsHaveCost = tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false; + bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment(); ContextMenu shared = ContextMenu.Shared; - var consist = car.EnumerateCoupled(LogicalEnd.A); - shared.AddButton(ContextMenuQuadrant.Unused2, $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate + shared.AddButton(ContextMenuQuadrant.Unused2, $"{(car._set.Cars.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate { CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost); }); - if (consist.Any(c => c.EndAirSystemIssue())) + if (car._set.Cars.Any(c => c.EndAirSystemIssue())) { shared.AddButton(ContextMenuQuadrant.Unused2, $"Air Up Consist", SpriteName.Select, delegate { @@ -37,7 +35,7 @@ internal class CarPickable_HandleShowContextMenu_Patch }); } - if (consist.Any(c => c.SupportsBleed())) + if (car._set.Cars.Any(c => c.SupportsBleed())) { shared.AddButton(ContextMenuQuadrant.Unused2, $"Bleed Consist", SpriteName.Bleed, delegate { diff --git a/TweaksAndThings/Settings/Settings.cs b/TweaksAndThings/Settings/Settings.cs index 9e3a1cd..4d51ffa 100644 --- a/TweaksAndThings/Settings/Settings.cs +++ b/TweaksAndThings/Settings/Settings.cs @@ -2,6 +2,9 @@ using System.Collections.Generic; using System.Linq; using RMROC451.TweaksAndThings.Enums; +using UI.Builder; +using Model; +using RMROC451.TweaksAndThings.Extensions; namespace RMROC451.TweaksAndThings; @@ -18,19 +21,26 @@ public class Settings List webhookSettingsList, bool handBrakeAndAirTagModifiers, RosterFuelColumnSettings engineRosterFuelColumnSettings, - bool endGearHelpersRequirePayment + bool endGearHelpersRequirePayment, + bool requireConsistCabooseForOilerAndHotboxSpotter, + bool cabooseAllowsConsistInfo ) { WebhookSettingsList = webhookSettingsList; HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers; EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings; EndGearHelpersRequirePayment = endGearHelpersRequirePayment; + RequireConsistCabooseForOilerAndHotboxSpotter = requireConsistCabooseForOilerAndHotboxSpotter; + CabooseAllowsConsistInfo = cabooseAllowsConsistInfo; } + public readonly UIState _selectedTabState = new UIState(null); public List? WebhookSettingsList; public bool HandBrakeAndAirTagModifiers; public RosterFuelColumnSettings? EngineRosterFuelColumnSettings; public bool EndGearHelpersRequirePayment; + public bool RequireConsistCabooseForOilerAndHotboxSpotter; + public bool CabooseAllowsConsistInfo; internal void AddAnotherRow() { @@ -82,15 +92,21 @@ public static class SettingsExtensions { public static List SanitizeEmptySettings(this IEnumerable? settings) { - List output = - settings?.Where(s => !string.IsNullOrEmpty(s.WebhookUrl))?.ToList() ?? + List output = + settings?.Where(s => !string.IsNullOrEmpty(s.WebhookUrl))?.ToList() ?? new(); output.Add(new()); return output; } - + + public static bool CabooseAllowsConsistInfo(this TweaksAndThingsPlugin input) => + input?.settings?.CabooseAllowsConsistInfo ?? false; + public static bool EndGearHelpersRequirePayment(this TweaksAndThingsPlugin input) => + input?.settings?.EndGearHelpersRequirePayment ?? false; + public static bool RequireConsistCabooseForOilerAndHotboxSpotter(this TweaksAndThingsPlugin input) => + input?.settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false; public static bool CabooseNonMotiveAllowedSetting(this TweaksAndThingsPlugin input, Car car) => input.EndGearHelpersRequirePayment() && car.set.Cars.CabooseInConsist() && car.NotMotivePower(); diff --git a/TweaksAndThings/TweaksAndThingsPlugin.cs b/TweaksAndThings/TweaksAndThingsPlugin.cs index d1e0380..30d488a 100644 --- a/TweaksAndThings/TweaksAndThingsPlugin.cs +++ b/TweaksAndThings/TweaksAndThingsPlugin.cs @@ -82,16 +82,73 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, settings.WebhookSettingsList = settings?.WebhookSettingsList.SanitizeEmptySettings(); - //WebhookUISection(ref builder); - //builder.AddExpandingVerticalSpacer(); - WebhooksListUISection(ref builder); - builder.AddExpandingVerticalSpacer(); - HandbrakesAndAnglecocksUISection(ref builder); - builder.AddExpandingVerticalSpacer(); - EnginRosterShowsFuelStatusUISection(ref builder); + builder.AddTabbedPanels(settings._selectedTabState, delegate (UITabbedPanelBuilder tabBuilder) + { + tabBuilder.AddTab("Caboose Mods", "cabooseUpdates", CabooseMods); + tabBuilder.AddTab("UI", "rosterUi", UiUpdates); + tabBuilder.AddTab("Webhooks", "webhooks", WebhooksListUISection); + }); } - private void EnginRosterShowsFuelStatusUISection(ref UIPanelBuilder builder) + private void CabooseMods(UIPanelBuilder builder) + { + builder.AddField( + "Caboose Use", + builder.AddToggle( + () => settings?.EndGearHelpersRequirePayment ?? false, + delegate (bool enabled) + { + if (settings == null) settings = new(); + settings.EndGearHelpersRequirePayment = enabled; + builder.Rebuild(); + } + ) + ).Tooltip("Enable End Gear Helper Cost", @$"Will cost 1 minute of AI Brake Crew & Caboose Crew time per car in the consist when the new inspector buttons are utilized. + +1.5x multiplier penalty to AI Brake Crew cost if no sufficiently crewed caboose nearby. + +Caboose starts reloading `Crew Hours` at any Team or Repair track (no waybill), after being stationary for 30 seconds. + +AutoOiler Update: Increases limit that crew will oiling a car from 75% -> 99%, also halves the time it takes (simulating crew from lead end and caboose handling half the train) + +AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - 30 seconds (Safety Is Everyone's Job)"); + + builder.AddField( + $"AutoAI\nRequirement", + builder.AddToggle( + () => settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false, + delegate (bool enabled) + { + if (settings == null) settings = new(); + settings.RequireConsistCabooseForOilerAndHotboxSpotter = enabled; + builder.Rebuild(); + } + ) + ).Tooltip("AI Engineer Requires Caboose", $@"A caboose is required in the consist to check for Hotboxes and perform Auto Oiler, if checked."); + } + + private void UiUpdates(UIPanelBuilder builder) + { + builder.AddField( + "Enable Tag Updates", + builder.AddToggle( + () => settings?.HandBrakeAndAirTagModifiers ?? false, + delegate (bool enabled) + { + if (settings == null) settings = new(); + settings.HandBrakeAndAirTagModifiers = enabled; + builder.Rebuild(); + } + ) + ).Tooltip("Enable Tag Updates", $@"Will suffix tag title with: +{TextSprites.CycleWaybills} if Air System issue. +{TextSprites.HandbrakeWheel} if there is a handbrake set. +{TextSprites.Hotbox} if a hotbox."); + + EngineRosterShowsFuelStatusUISection(builder); + } + + private void EngineRosterShowsFuelStatusUISection(UIPanelBuilder builder) { var columns = Enum.GetValues(typeof(EngineRosterFuelDisplayColumn)).Cast().Select(i => i.ToString()).ToList(); builder.AddSection("Fuel Display in Engine Roster", delegate (UIPanelBuilder builder) @@ -123,39 +180,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, }); } - private void HandbrakesAndAnglecocksUISection(ref UIPanelBuilder builder) - { - builder.AddSection("Tag Callout Handbrake and Air System Helper", delegate (UIPanelBuilder builder) - { - builder.AddField( - "Enable Tag Updates", - builder.AddToggle( - () => settings?.HandBrakeAndAirTagModifiers ?? false, - delegate (bool enabled) - { - if (settings == null) settings = new(); - settings.HandBrakeAndAirTagModifiers = enabled; - builder.Rebuild(); - } - ) - ).Tooltip("Enable Tag Updates", $"Will add {TextSprites.CycleWaybills} to the car tag title having Air System issues. Also prepends {TextSprites.HandbrakeWheel} if there is a handbrake set.\n\nHolding Left Alt while tags are displayed only shows tag titles that have issues."); - - builder.AddField( - "Caboose Use", - builder.AddToggle( - () => settings?.EndGearHelpersRequirePayment ?? false, - delegate (bool enabled) - { - if (settings == null) settings = new(); - settings.EndGearHelpersRequirePayment = enabled; - builder.Rebuild(); - } - ) - ).Tooltip("Enable End Gear Helper Cost", $"Will cost 1 minute of AI Brake Crew & Caboose Crew time per car in the consist when the new inspector buttons are utilized.\n\n1.5x multiplier penalty to AI Brake Crew cost if no sufficiently crewed caboose nearby.\n\nCaboose starts reloading `Crew Hours` at any Team or Repair track (no waybill), after being stationary for 30 seconds."); - }); - } - - private void WebhooksListUISection(ref UIPanelBuilder builder) + private void WebhooksListUISection(UIPanelBuilder builder) { builder.AddSection("Webhooks List", delegate (UIPanelBuilder builder) { diff --git a/updates.json b/updates.json new file mode 100644 index 0000000..638be2c --- /dev/null +++ b/updates.json @@ -0,0 +1,12 @@ +{ + "RMROC451.TweaksAndThings": { + "version": "1.0.0", + "changelog": [ + { + "version": "*", + "date": "2024-07-26T14:31:49.0948925Z", + "desc": "https://github.com/rmroc451/TweaksAndThings/releases" + } + ] + } +} \ No newline at end of file