#29 #30 Allow caboose setting to require that a caboose is present in the consist to use the AI Engineer's AutoOiler & AutoHotboxSpotter.

This commit is contained in:
2024-07-26 12:25:03 -05:00
parent 5eed492b47
commit 5774c7f04c
8 changed files with 135 additions and 64 deletions

View File

@@ -6,6 +6,7 @@
<!-- Copy the mod to the game directory --> <!-- Copy the mod to the game directory -->
<GameModDir Condition="'$(GameModDir)' == ''">$(GameDir)/Mods/$(AssemblyName)</GameModDir> <GameModDir Condition="'$(GameModDir)' == ''">$(GameDir)/Mods/$(AssemblyName)</GameModDir>
<OutDir Condition="'$(Configuration)' == 'Debug'">$(GameModDir)/</OutDir> <OutDir Condition="'$(Configuration)' == 'Debug'">$(GameModDir)/</OutDir>
<VersionTimestamp>$([System.DateTime]::UtcNow.ToString(`o`))</VersionTimestamp>
</PropertyGroup> </PropertyGroup>
<!-- Replace the default version if something was set for it --> <!-- Replace the default version if something was set for it -->
@@ -30,7 +31,7 @@
<!-- Publish the mod as a neat zip file --> <!-- Publish the mod as a neat zip file -->
<Target Name="PrepareForPublishing" AfterTargets="AfterBuild" Condition="'$(Configuration)' == 'Release'"> <Target Name="PrepareForPublishing" AfterTargets="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<!-- Replace $(AssemblyVersion) with the actual version --> <!-- Replace $(AssemblyVersion) with the actual version -->
<Exec Command="powershell -Command &quot;(Get-Content '$(OutputPath)Definition.json') -replace '\$\(AssemblyVersion\)', '$(AssemblyVersion)' | Set-Content '$(OutputPath)Definition.json'&quot;" /> <Exec Command="powershell -Command &quot;(Get-Content '$(OutputPath)Definition.json') -replace '\$\(AssemblyVersion\)', '$(AssemblyVersion)_$(VersionTimestamp)' | Set-Content '$(OutputPath)Definition.json'&quot;" />
<PropertyGroup> <PropertyGroup>
<ModsDirectory>$(OutputPath)/Mods</ModsDirectory> <ModsDirectory>$(OutputPath)/Mods</ModsDirectory>

View File

@@ -12,7 +12,7 @@ namespace RMROC451.TweaksAndThings.Extensions
private static float CabooseAutoOilerLimit(this bool hasCaboose) => private static float CabooseAutoOilerLimit(this bool hasCaboose) =>
hasCaboose ? 0.99f : AutoOiler.OilIfBelow; 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(); int originIndex = oiler.FindOriginIndex();
bool hasCaboose = oiler._cars.CabooseInConsist(); bool hasCaboose = oiler._cars.CabooseInConsist();
@@ -21,13 +21,17 @@ namespace RMROC451.TweaksAndThings.Extensions
_log.Error("Couldn't find origin car {car}", oiler._originCar); _log.Error("Couldn't find origin car {car}", oiler._originCar);
oiler._coroutine = null; oiler._coroutine = null;
yield break; 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; oiler._reverse = originIndex > oiler._cars.Count - originIndex;
_log.Information( _log.Information(
"AutoOiler {name} starting, rev = {reverse}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}", "AutoOiler {name} starting, rev = {reverse}, caboose required = {req}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}",
oiler.name, oiler.name,
oiler._reverse, oiler._reverse,
hasCaboose, cabooseRequired,
hasCaboose,
hasCaboose.CabooseAutoOilerLimit() hasCaboose.CabooseAutoOilerLimit()
); );
while (true) 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) while (true)
{ {
@@ -73,7 +77,11 @@ namespace RMROC451.TweaksAndThings.Extensions
yield return new WaitForSeconds(1f); yield return new WaitForSeconds(1f);
continue; 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(); spotter.CheckForHotbox();
while (spotter.HasCars) while (spotter.HasCars)
{ {
@@ -82,12 +90,21 @@ namespace RMROC451.TweaksAndThings.Extensions
{ {
var numOrig = num; var numOrig = num;
num = Random.Range(15, 30); 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); yield return new WaitForSeconds(num);
spotter.CheckForHotbox(); 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;
}
} }
} }

View File

@@ -18,9 +18,10 @@ internal class AutoHotboxSpotter_SpotterLoop_Patch
{ {
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared; TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return true; 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 return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
} }
} }

View File

@@ -18,9 +18,10 @@ internal class AutoOiler_Loop_Patch
{ {
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared; TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return true; 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 return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
} }
} }

View File

@@ -7,7 +7,6 @@ using RollingStock;
using System.Linq; using System.Linq;
using UI; using UI;
using UI.ContextMenu; using UI.ContextMenu;
using static Model.Car;
namespace RMROC451.TweaksAndThings.Patches; namespace RMROC451.TweaksAndThings.Patches;
@@ -21,15 +20,14 @@ internal class CarPickable_HandleShowContextMenu_Patch
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared; TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return; if (!tweaksAndThings.IsEnabled) return;
bool buttonsHaveCost = tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false; bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
ContextMenu shared = ContextMenu.Shared; ContextMenu shared = ContextMenu.Shared;
var consist = car.EnumerateCoupled(LogicalEnd.A); shared.AddButton(ContextMenuQuadrant.Unused2, $"{(car._set.Cars.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate
shared.AddButton(ContextMenuQuadrant.Unused2, $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate
{ {
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost); 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 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 shared.AddButton(ContextMenuQuadrant.Unused2, $"Bleed Consist", SpriteName.Bleed, delegate
{ {

View File

@@ -2,6 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using RMROC451.TweaksAndThings.Enums; using RMROC451.TweaksAndThings.Enums;
using UI.Builder;
using Model;
using RMROC451.TweaksAndThings.Extensions;
namespace RMROC451.TweaksAndThings; namespace RMROC451.TweaksAndThings;
@@ -18,19 +21,26 @@ public class Settings
List<WebhookSettings> webhookSettingsList, List<WebhookSettings> webhookSettingsList,
bool handBrakeAndAirTagModifiers, bool handBrakeAndAirTagModifiers,
RosterFuelColumnSettings engineRosterFuelColumnSettings, RosterFuelColumnSettings engineRosterFuelColumnSettings,
bool endGearHelpersRequirePayment bool endGearHelpersRequirePayment,
bool requireConsistCabooseForOilerAndHotboxSpotter,
bool cabooseAllowsConsistInfo
) )
{ {
WebhookSettingsList = webhookSettingsList; WebhookSettingsList = webhookSettingsList;
HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers; HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers;
EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings; EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings;
EndGearHelpersRequirePayment = endGearHelpersRequirePayment; EndGearHelpersRequirePayment = endGearHelpersRequirePayment;
RequireConsistCabooseForOilerAndHotboxSpotter = requireConsistCabooseForOilerAndHotboxSpotter;
CabooseAllowsConsistInfo = cabooseAllowsConsistInfo;
} }
public readonly UIState<string> _selectedTabState = new UIState<string>(null);
public List<WebhookSettings>? WebhookSettingsList; public List<WebhookSettings>? WebhookSettingsList;
public bool HandBrakeAndAirTagModifiers; public bool HandBrakeAndAirTagModifiers;
public RosterFuelColumnSettings? EngineRosterFuelColumnSettings; public RosterFuelColumnSettings? EngineRosterFuelColumnSettings;
public bool EndGearHelpersRequirePayment; public bool EndGearHelpersRequirePayment;
public bool RequireConsistCabooseForOilerAndHotboxSpotter;
public bool CabooseAllowsConsistInfo;
internal void AddAnotherRow() internal void AddAnotherRow()
{ {
@@ -82,15 +92,21 @@ public static class SettingsExtensions
{ {
public static List<WebhookSettings> SanitizeEmptySettings(this IEnumerable<WebhookSettings>? settings) public static List<WebhookSettings> SanitizeEmptySettings(this IEnumerable<WebhookSettings>? settings)
{ {
List<WebhookSettings> output = List<WebhookSettings> output =
settings?.Where(s => !string.IsNullOrEmpty(s.WebhookUrl))?.ToList() ?? settings?.Where(s => !string.IsNullOrEmpty(s.WebhookUrl))?.ToList() ??
new(); new();
output.Add(new()); output.Add(new());
return output; 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) => public static bool CabooseNonMotiveAllowedSetting(this TweaksAndThingsPlugin input, Car car) =>
input.EndGearHelpersRequirePayment() && car.set.Cars.CabooseInConsist() && car.NotMotivePower(); input.EndGearHelpersRequirePayment() && car.set.Cars.CabooseInConsist() && car.NotMotivePower();

View File

@@ -82,16 +82,73 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
settings.WebhookSettingsList = settings.WebhookSettingsList =
settings?.WebhookSettingsList.SanitizeEmptySettings(); settings?.WebhookSettingsList.SanitizeEmptySettings();
//WebhookUISection(ref builder); builder.AddTabbedPanels(settings._selectedTabState, delegate (UITabbedPanelBuilder tabBuilder)
//builder.AddExpandingVerticalSpacer(); {
WebhooksListUISection(ref builder); tabBuilder.AddTab("Caboose Mods", "cabooseUpdates", CabooseMods);
builder.AddExpandingVerticalSpacer(); tabBuilder.AddTab("UI", "rosterUi", UiUpdates);
HandbrakesAndAnglecocksUISection(ref builder); tabBuilder.AddTab("Webhooks", "webhooks", WebhooksListUISection);
builder.AddExpandingVerticalSpacer(); });
EnginRosterShowsFuelStatusUISection(ref builder);
} }
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<EngineRosterFuelDisplayColumn>().Select(i => i.ToString()).ToList(); var columns = Enum.GetValues(typeof(EngineRosterFuelDisplayColumn)).Cast<EngineRosterFuelDisplayColumn>().Select(i => i.ToString()).ToList();
builder.AddSection("Fuel Display in Engine Roster", delegate (UIPanelBuilder builder) builder.AddSection("Fuel Display in Engine Roster", delegate (UIPanelBuilder builder)
@@ -123,39 +180,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
}); });
} }
private void HandbrakesAndAnglecocksUISection(ref UIPanelBuilder builder) private void WebhooksListUISection(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)
{ {
builder.AddSection("Webhooks List", delegate (UIPanelBuilder builder) builder.AddSection("Webhooks List", delegate (UIPanelBuilder builder)
{ {

12
updates.json Normal file
View File

@@ -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"
}
]
}
}