Merge pull request #31 from rmroc451/v1.0.0-prep

Closes #27 fix issue with last car end gear issue detection
Closes #28 add hotbox icon to tag update info
Closes #25 adding follow to car context menu
Closes #24 adding consist info to non motive power cars, that have a caboose when Caboose Use mod setting is enabled.
Closes #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:40:38 -05:00
committed by GitHub
11 changed files with 213 additions and 99 deletions

View File

@@ -2,17 +2,18 @@
<PropertyGroup>
<!-- Try to find the directory on our own -->
<GameManagedDir Condition="'$(GameManagedDir)' == ''">$([System.IO.Directory]::GetDirectories(`$(GameDir)`, `*_Data`)[0])\Managed</GameManagedDir>
<!-- Copy the mod to the game directory -->
<GameModDir Condition="'$(GameModDir)' == ''">$(GameDir)/Mods/$(AssemblyName)</GameModDir>
<OutDir Condition="'$(Configuration)' == 'Debug'">$(GameModDir)/</OutDir>
<VersionTimestamp>$([System.DateTime]::UtcNow.ToString(`o`))</VersionTimestamp>
</PropertyGroup>
<!-- Replace the default version if something was set for it -->
<PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''">
<MajorVersion Condition="'$(MajorVersion)' == ''">0</MajorVersion>
<MinorVersion Condition="'$(MinorVersion)' == ''">1</MinorVersion>
<PatchVersion Condition="'$(PatchVersion)' == ''">8</PatchVersion>
<MajorVersion Condition="'$(MajorVersion)' == ''">1</MajorVersion>
<MinorVersion Condition="'$(MinorVersion)' == ''">0</MinorVersion>
<PatchVersion Condition="'$(PatchVersion)' == ''">0</PatchVersion>
<AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<ProductVersion>$(AssemblyVersion)</ProductVersion>
@@ -30,7 +31,7 @@
<!-- Publish the mod as a neat zip file -->
<Target Name="PrepareForPublishing" AfterTargets="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<!-- 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>
<ModsDirectory>$(OutputPath)/Mods</ModsDirectory>
@@ -43,15 +44,15 @@
<!-- Assure the output path exists -->
<MakeDir Directories="$(PublishPath)" />
<!-- Remove the old Mods directory, and Zips file if any is lying around -->
<ItemGroup>
<OldZips Include="$(PublishPath)/$(AssemblyName)_*.zip" />
</ItemGroup>
<RemoveDir Directories="$(ModsDirectory)" ContinueOnError="true" />
<Delete Files="@(OldZips)" />
<!-- Create the Mods directory again -->
<MakeDir Directories="$(ModDirectory)" />
@@ -64,7 +65,7 @@
<!-- Zip it up -->
<Exec Command="powershell -Command &quot;Compress-Archive -Path '$(ModsDirectory)' -Destination '$(ZipName)'&quot;" />
<!-- Move them to the game directory -->
<Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(GameModDir)" />
</Target>

View File

@@ -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;
}
}
}

View File

@@ -12,10 +12,14 @@ namespace RMROC451.TweaksAndThings.Extensions;
public static class Car_Extensions
{
private static bool EndGearIssue(this Car car, Car.LogicalEnd end) =>
(!car[end].IsCoupled && car[end].IsAnglecockOpen) ||
(car[end].IsCoupled && !car[end].IsAirConnectedAndOpen);
public static bool EndAirSystemIssue(this Car car)
{
bool AEndAirSystemIssue = car[Car.LogicalEnd.A].IsCoupled && !car[Car.LogicalEnd.A].IsAirConnectedAndOpen;
bool BEndAirSystemIssue = car[Car.LogicalEnd.B].IsCoupled && !car[Car.LogicalEnd.B].IsAirConnectedAndOpen;
bool AEndAirSystemIssue = car.EndGearIssue(Car.LogicalEnd.A);
bool BEndAirSystemIssue = car.EndGearIssue(Car.LogicalEnd.B);
bool EndAirSystemIssue = AEndAirSystemIssue || BEndAirSystemIssue;
return EndAirSystemIssue;
}
@@ -104,18 +108,18 @@ public static class Car_Extensions
Rect rect = new Rect(new Vector2(center.x - 30f, center.z - 30f), Vector2.one * 30f * 2f);
var cars = tc.CarIdsInRect(rect);
Log.Information($"{nameof(HuntingForCabeeseNearCar)} => {cars.Count()}");
List<(string carId, float distance)> source =
List<(string carId, float distance)> source =
cars
.Select(carId =>
{
Car car = tc.CarForId(carId);
if (car == null || !car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready))
{
Car car = tc.CarForId(carId);
if (car == null || !car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready))
{
return (carId: carId, distance: 1000f);
}
Vector3 a = WorldTransformer.WorldToGame(car.GetMotionSnapshot().Position);
return (carId: carId, distance: Vector3.Distance(a, center));
}).ToList();
return (carId: carId, distance: 1000f);
}
Vector3 a = WorldTransformer.WorldToGame(car.GetMotionSnapshot().Position);
return (carId: carId, distance: Vector3.Distance(a, center));
}).ToList();
return source;
}
}

View File

@@ -18,9 +18,10 @@ internal class AutoHotboxSpotter_SpotterLoop_Patch
{
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.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
}
}

View File

@@ -18,9 +18,10 @@ internal class AutoOiler_Loop_Patch
{
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.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
}
}

View File

@@ -1,9 +1,9 @@
using Game.Messages;
using Core;
using Game.Messages;
using Game.State;
using HarmonyLib;
using KeyValue.Runtime;
using Model;
using Model.OpsNew;
using Network;
using Railloader;
using RMROC451.TweaksAndThings.Enums;
@@ -13,6 +13,7 @@ using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using UI;
using UI.Builder;
using UI.CarInspector;
using UI.ContextMenu;
@@ -40,36 +41,61 @@ internal class CarInspector_PopulateCarPanel_Patch
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return true;
bool buttonsHaveCost = tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false;
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
var consist = __instance._car.EnumerateCoupled(LogicalEnd.A);
var consist = __instance._car._set.Cars;
builder = AddCarConsistRebuildObservers(builder, consist);
builder.HStack(delegate (UIPanelBuilder hstack)
{
var buttonName = $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} {TextSprites.HandbrakeWheel}";
hstack.AddButtonCompact(buttonName, delegate {
hstack.AddButtonCompact(buttonName, delegate
{
MrocConsistHelper(__instance._car, MrocHelperType.Handbrake, buttonsHaveCost);
hstack.Rebuild();
}).Tooltip(buttonName, $"Iterates over cars in this consist and {(consist.Any(c => c.HandbrakeApplied()) ? "releases" : "sets")} {TextSprites.HandbrakeWheel}.");
if (consist.Any(c => c.EndAirSystemIssue()))
{
hstack.AddButtonCompact("Connect Air", delegate {
hstack.AddButtonCompact("Connect Air", delegate
{
MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
hstack.Rebuild();
}).Tooltip("Connect Consist Air", "Iterates over each car in this consist and connects gladhands and opens anglecocks.");
}
hstack.AddButtonCompact("Bleed Consist", delegate {
hstack.AddButtonCompact("Bleed Consist", delegate
{
MrocConsistHelper(__instance._car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
hstack.Rebuild();
}).Tooltip("Bleed Air Lines", "Iterates over each car in this consist and bleeds the air out of the lines.");
});
CabooseUiEnhancer(__instance, builder, consist, tweaksAndThings);
return true;
}
private static void CabooseUiEnhancer(CarInspector __instance, UIPanelBuilder builder, IEnumerable<Car> consist, TweaksAndThingsPlugin plugin)
{
if (plugin.CabooseNonMotiveAllowedSetting(__instance._car))
{
builder.HStack(delegate (UIPanelBuilder hstack)
{
hstack.AddField("Consist Info", hstack.HStack(delegate (UIPanelBuilder field)
{
int consistLength = consist.Count();
int tonnage = LocomotiveControlsHoverArea.CalculateTonnage(consist);
int lengthInMeters = UnityEngine.Mathf.CeilToInt(LocomotiveControlsHoverArea.CalculateLengthInMeters(consist.ToList()) * 3.28084f);
var newSubTitle = () => string.Format("{0}, {1:N0}T, {2:N0}ft, {3:0.0} mph", consistLength.Pluralize("car"), tonnage, lengthInMeters, __instance._car.VelocityMphAbs);
field.AddLabel(() => newSubTitle(), UIPanelBuilder.Frequency.Fast)
.Tooltip("Consist Info", "Reflects info about consist.").FlexibleWidth();
}));
});
}
}
private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist)
{
TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>();
@@ -100,7 +126,7 @@ internal class CarInspector_PopulateCarPanel_Patch
if (car.TagCallout != null) tagController.UpdateTags(CameraSelector.shared._currentCamera.GroundPosition, true); //tagController.UpdateTag(car, car.TagCallout, OpsController.Shared);
if (ContextMenu.IsShown && ContextMenu.Shared.centerLabel.text == car.DisplayName) CarPickable.HandleShowContextMenu(car);
}
catch(Exception ex)
catch (Exception ex)
{
_log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
}
@@ -114,7 +140,7 @@ internal class CarInspector_PopulateCarPanel_Patch
//var dh = new DownloadHandlerAudioClip($"file://{cacheFileName}", AudioType.MPEG);
//dh.compressed = true; // This
//using (UnityWebRequest wr = new UnityWebRequest($"file://{cacheFileName}", "GET", dh, null)) {
// yield return wr.SendWebRequest();
// if (wr.responseCode == 200) {
@@ -125,7 +151,7 @@ internal class CarInspector_PopulateCarPanel_Patch
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost)
{
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
IEnumerable<Model.Car> consist = car.EnumerateCoupled(LogicalEnd.A);
IEnumerable<Model.Car> consist = car._set.Cars;
_log.ForContext("car", car).Verbose($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
CalculateCostIfEnabled(car, mrocHelperType, buttonsHaveCost, consist);
@@ -194,7 +220,7 @@ internal class CarInspector_PopulateCarPanel_Patch
StateManager_OnDayDidChange_Patch.UnbilledAutoBrakeCrewRunDuration += timeCost;
}
}
}
public static Car? NearbyCabooseWithAvailableCrew(Car car, float timeNeeded, bool decrement = false)
{
@@ -206,7 +232,7 @@ internal class CarInspector_PopulateCarPanel_Patch
carIdsCheckedAlready.Add(car.id);
//check consist, for cabeese
IEnumerable<Car> consist = car.EnumerateCoupled(LogicalEnd.A);
IEnumerable<Car> consist = car._set.Cars;
output = consist.FirstOrDefault(c => c.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement));
if (output != null) return output; //short out if we are good
carIdsCheckedAlready.UnionWith(consist.Select(c => c.id));

View File

@@ -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<TweaksAndThingsPlugin>.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
{
@@ -45,6 +43,11 @@ internal class CarPickable_HandleShowContextMenu_Patch
});
}
shared.AddButton(ContextMenuQuadrant.Unused2, $"Follow", SpriteName.Inspect, delegate
{
CameraSelector.shared.FollowCar(car);
});
shared.BuildItemAngles();
shared.StartCoroutine(shared.AnimateButtonsShown());
}

View File

@@ -3,6 +3,8 @@ using Model;
using Model.OpsNew;
using Railloader;
using RMROC451.TweaksAndThings.Extensions;
using System.Collections.Generic;
using System.Linq;
using UI.Tags;
namespace RMROC451.TweaksAndThings.Patches;
@@ -32,13 +34,16 @@ internal class TagController_UpdateTag_Patch
private static void ProceedWithPostFix(Car car, TagCallout tagCallout)
{
tagCallout.callout.Title = string.Format(tagTitleFormat, "{0}", car.DisplayName);
List<string> tags = [];
if (car.HasHotbox) tags.Add(TextSprites.Hotbox);
if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
tagCallout.callout.Title =
(car.CarAndEndGearIssue(), car.EndAirSystemIssue(), car.HandbrakeApplied()) switch
tags.Any() switch
{
(true, _, _) => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{TextSprites.CycleWaybills}{TextSprites.HandbrakeWheel}".Replace("{0}", "2"),
(_, true, _) => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{TextSprites.CycleWaybills}".Replace("{0}", "1"),
(_, _, true) => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{TextSprites.HandbrakeWheel}".Replace("{0}", "1"),
true => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{string.Join("", tags)}".Replace("{0}", tags.Count().ToString()),
_ => car.DisplayName
};
}

View File

@@ -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<WebhookSettings> 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<string> _selectedTabState = new UIState<string>(null);
public List<WebhookSettings>? WebhookSettingsList;
public bool HandBrakeAndAirTagModifiers;
public RosterFuelColumnSettings? EngineRosterFuelColumnSettings;
public bool EndGearHelpersRequirePayment;
public bool RequireConsistCabooseForOilerAndHotboxSpotter;
public bool CabooseAllowsConsistInfo;
internal void AddAnotherRow()
{
@@ -82,13 +92,22 @@ public static class SettingsExtensions
{
public static List<WebhookSettings> SanitizeEmptySettings(this IEnumerable<WebhookSettings>? settings)
{
List<WebhookSettings> output =
settings?.Where(s => !string.IsNullOrEmpty(s.WebhookUrl))?.ToList() ??
List<WebhookSettings> 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();
}

View File

@@ -82,16 +82,73 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
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<EngineRosterFuelDisplayColumn>().Select(i => i.ToString()).ToList();
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)
{
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)
{

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