Compare commits

..

22 Commits

Author SHA1 Message Date
108900b026 Merge pull request #36 from rmroc451/33-double-click-to-follow-car
#33 double primary click any car/loco to follow
2024-07-27 22:33:18 -05:00
9e8c38e6f4 Merge pull request #35 from rmroc451/26-caboose-auto-heal-hotbox-when-fully-oiled
#26 auto heal hotbox when found if caboose use is enabled and caboose…
2024-07-27 22:32:49 -05:00
89f2490f14 Merge pull request #34 from rmroc451/32-buttons-dont-adjust-when-decoupling-as-expected
#32 swap from integration set that was giving inconsistent results, a…
2024-07-27 22:32:20 -05:00
fbd08b007c #33 double primary click any car/loco to follow 2024-07-27 22:31:33 -05:00
27d3432cbf #26 auto heal hotbox when found if caboose use is enabled and caboose is present in consist. 2024-07-27 22:30:40 -05:00
cf1d5e32e5 #32 swap from integration set that was giving inconsistent results, and adding in a panel.reubuildOnInterval when an observed value changes. 2024-07-27 22:25:23 -05:00
92317b29b2 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.
2024-07-26 12:40:38 -05:00
1f48bf04aa increment version number 2024-07-26 12:35:52 -05:00
5774c7f04c #29 #30 Allow caboose setting to require that a caboose is present in the consist to use the AI Engineer's AutoOiler & AutoHotboxSpotter. 2024-07-26 12:25:03 -05:00
5eed492b47 #24 adding consist info to non motive power cars, that have a caboose, when Caboose Use mod setting is enabled. 2024-07-26 12:20:52 -05:00
d4d18d8c92 #25 adding follow to car context menu 2024-07-26 10:17:09 -05:00
d7e35828b8 #28 add hotbox icon to tag update info 2024-07-26 10:15:09 -05:00
0b444d6364 #27 fix issue with last car end gear issue detection 2024-07-26 10:13:35 -05:00
d197ff8d8a Merge pull request #23 from rmroc451/misc-fixes-and-caboose-additions
Misc fixes and caboose additions
2024-07-24 00:09:54 -05:00
89894890b4 build version increment 2024-07-24 00:07:59 -05:00
a13701c3d2 AutoHotboxSpotter Patch for caboose consist presence, making checks happen between every 15-30 seconds vs 60-300 (default) 2024-07-24 00:07:48 -05:00
b5be17703f Adding AutoOiler Patch that halves times to oil consists and the start delay, if a caboose is presentin the consist that the ai engineer is in. 2024-07-24 00:06:37 -05:00
35afa4520d log context adjustments for CarInspector patch class 2024-07-24 00:04:49 -05:00
80d064c950 remove SetActive call, which seems to be a performance gain when tags are displayed. 2024-07-24 00:03:11 -05:00
ec21effd30 Adding consist buttons from inspector to new context menu, and squashing bug that would occur if inspector open but tags weren't active, breaking the ui panel builder reload detection for handbrake/couple/air changes.
final change of Version 0.1.7
2024-07-13 13:09:42 -05:00
2242aaacde Merge pull request #22 from rmroc451/1.6.1
Fix issue with cars that are missing tagcallouts from throwing errors.
2024-07-13 11:12:24 -05:00
e26713688b Fix issue with cars that are missing tagcallouts from throwing errors. 2024-07-13 11:11:22 -05:00
12 changed files with 483 additions and 106 deletions

View File

@@ -2,17 +2,18 @@
<PropertyGroup> <PropertyGroup>
<!-- Try to find the directory on our own --> <!-- Try to find the directory on our own -->
<GameManagedDir Condition="'$(GameManagedDir)' == ''">$([System.IO.Directory]::GetDirectories(`$(GameDir)`, `*_Data`)[0])\Managed</GameManagedDir> <GameManagedDir Condition="'$(GameManagedDir)' == ''">$([System.IO.Directory]::GetDirectories(`$(GameDir)`, `*_Data`)[0])\Managed</GameManagedDir>
<!-- 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 -->
<PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''"> <PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''">
<MajorVersion Condition="'$(MajorVersion)' == ''">0</MajorVersion> <MajorVersion Condition="'$(MajorVersion)' == ''">1</MajorVersion>
<MinorVersion Condition="'$(MinorVersion)' == ''">1</MinorVersion> <MinorVersion Condition="'$(MinorVersion)' == ''">0</MinorVersion>
<PatchVersion Condition="'$(PatchVersion)' == ''">6</PatchVersion> <PatchVersion Condition="'$(PatchVersion)' == ''">0</PatchVersion>
<AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion> <AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion> <FileVersion>$(AssemblyVersion)</FileVersion>
<ProductVersion>$(AssemblyVersion)</ProductVersion> <ProductVersion>$(AssemblyVersion)</ProductVersion>
@@ -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>
@@ -43,15 +44,15 @@
<!-- Assure the output path exists --> <!-- Assure the output path exists -->
<MakeDir Directories="$(PublishPath)" /> <MakeDir Directories="$(PublishPath)" />
<!-- Remove the old Mods directory, and Zips file if any is lying around --> <!-- Remove the old Mods directory, and Zips file if any is lying around -->
<ItemGroup> <ItemGroup>
<OldZips Include="$(PublishPath)/$(AssemblyName)_*.zip" /> <OldZips Include="$(PublishPath)/$(AssemblyName)_*.zip" />
</ItemGroup> </ItemGroup>
<RemoveDir Directories="$(ModsDirectory)" ContinueOnError="true" /> <RemoveDir Directories="$(ModsDirectory)" ContinueOnError="true" />
<Delete Files="@(OldZips)" /> <Delete Files="@(OldZips)" />
<!-- Create the Mods directory again --> <!-- Create the Mods directory again -->
<MakeDir Directories="$(ModDirectory)" /> <MakeDir Directories="$(ModDirectory)" />
@@ -64,7 +65,7 @@
<!-- Zip it up --> <!-- Zip it up -->
<Exec Command="powershell -Command &quot;Compress-Archive -Path '$(ModsDirectory)' -Destination '$(ZipName)'&quot;" /> <Exec Command="powershell -Command &quot;Compress-Archive -Path '$(ModsDirectory)' -Destination '$(ZipName)'&quot;" />
<!-- Move them to the game directory --> <!-- Move them to the game directory -->
<Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(GameModDir)" /> <Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(GameModDir)" />
</Target> </Target>

View File

@@ -0,0 +1,118 @@
using Model.AI;
using System.Collections;
using UnityEngine;
namespace RMROC451.TweaksAndThings.Extensions
{
internal static class AutoEngineer_Extensions
{
private static float CabooseHalvedFloat(this float input, Model.Car? hasCaboose) =>
hasCaboose ? input / 2 : input;
private static float CabooseAutoOilerLimit(this Model.Car? caboose) =>
caboose ? 0.99f : AutoOiler.OilIfBelow;
public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log, bool cabooseRequired)
{
int originIndex = oiler.FindOriginIndex();
Model.Car? foundCaboose = oiler._cars.CabooseInConsist();
if (originIndex < 0)
{
_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, foundCaboose, _log))
{
yield break;
}
oiler._reverse = originIndex > oiler._cars.Count - originIndex;
_log.Information(
"AutoOiler {name} starting, rev = {reverse}, caboose required = {req}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}",
oiler.name,
oiler._reverse,
cabooseRequired,
foundCaboose,
foundCaboose.CabooseAutoOilerLimit()
);
while (true)
{
yield return new WaitForSeconds(AutoOiler.StartDelay.CabooseHalvedFloat(foundCaboose));
int carIndex = originIndex;
float adjustedTimeToWalk = AutoOiler.TimeToWalkCar.CabooseHalvedFloat(foundCaboose);
do
{
if (oiler.TryGetCar(carIndex, out var car))
{
float num = 0f;
float origOil = car.Oiled;
if (car.NeedsOiling && car.Oiled < foundCaboose.CabooseAutoOilerLimit())
{
float num2 = 1f - car.Oiled;
car.OffsetOiled(num2);
float num3 = num2 * AutoOiler.TimeToFullyOil.CabooseHalvedFloat(foundCaboose);
num += num3;
oiler._pendingRunDuration += num3;
oiler._oiledCount++;
_log.Information("AutoOiler {name}: oiled {car} from {orig} => {new}", oiler.name, car, origOil, car.Oiled);
}
if (car.HasHotbox && car.Oiled == 1f && cabooseRequired && foundCaboose)
{
_log.Information("AutoOiler {name}: {foundCaboose} repaired hotbox {car}", oiler.name, foundCaboose, car);
car.AdjustHotboxValue(0f);
}
num += adjustedTimeToWalk;
oiler._pendingRunDuration += adjustedTimeToWalk;
yield return new WaitForSeconds(num);
}
carIndex = oiler.NextIndex(carIndex);
}
while (oiler.InBounds(carIndex));
oiler._reverse = !oiler._reverse;
oiler.PayWages();
}
}
public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log, bool cabooseRequired)
{
while (true)
{
Model.Car? foundCaboose = spotter._cars.CabooseInConsist();
if (!spotter.HasCars)
{
yield return new WaitForSeconds(1f);
continue;
}
_log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Found Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}",
spotter.name, foundCaboose, spotter.HasCars, cabooseRequired);
foundCaboose = spotter._cars.CabooseInConsist();
if (CabooseRequirementChecker(string.Format("{0} {1}", spotter.GetType().Name, spotter.name), cabooseRequired, foundCaboose, _log))
{
yield break;
}
spotter.CheckForHotbox();
while (spotter.HasCars)
{
int num = Random.Range(60, 300);
foundCaboose = spotter._cars.CabooseInConsist();
if (foundCaboose)
{
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}; Requires Caboose {requiresCaboose}", spotter.name, numOrig, num, foundCaboose, cabooseRequired);
}
yield return new WaitForSeconds(num);
spotter.CheckForHotbox();
}
}
}
private static bool CabooseRequirementChecker(string name, bool cabooseRequired, Model.Car? foundCaboose, Serilog.ILogger _log)
{
bool error = cabooseRequired && foundCaboose == null;
if (error) {
_log.Debug("{name}: Couldn't find required caboose!", name);
}
return error;
}
}
}

View File

@@ -1,4 +1,6 @@
using Helpers; using Game.Messages;
using Game.State;
using Helpers;
using Model; using Model;
using Model.Definition.Data; using Model.Definition.Data;
using Model.OpsNew; using Model.OpsNew;
@@ -12,10 +14,14 @@ namespace RMROC451.TweaksAndThings.Extensions;
public static class Car_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) public static bool EndAirSystemIssue(this Car car)
{ {
bool AEndAirSystemIssue = car[Car.LogicalEnd.A].IsCoupled && !car[Car.LogicalEnd.A].IsAirConnectedAndOpen; bool AEndAirSystemIssue = car.EndGearIssue(Car.LogicalEnd.A);
bool BEndAirSystemIssue = car[Car.LogicalEnd.B].IsCoupled && !car[Car.LogicalEnd.B].IsAirConnectedAndOpen; bool BEndAirSystemIssue = car.EndGearIssue(Car.LogicalEnd.B);
bool EndAirSystemIssue = AEndAirSystemIssue || BEndAirSystemIssue; bool EndAirSystemIssue = AEndAirSystemIssue || BEndAirSystemIssue;
return EndAirSystemIssue; return EndAirSystemIssue;
} }
@@ -56,7 +62,9 @@ public static class Car_Extensions
public static bool IsCaboose(this Car car) => car.Archetype == Model.Definition.CarArchetype.Caboose; public static bool IsCaboose(this Car car) => car.Archetype == Model.Definition.CarArchetype.Caboose;
public static bool CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(c => c.IsCaboose()); public static bool IsCabooseAndStoppedForLoadRefresh(this Car car) => car.IsCaboose() && car.IsStopped(30f);
public static Car? CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(c => c.IsCaboose());
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, HashSet<string> carIdsCheckedAlready, bool decrement = false) public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, HashSet<string> carIdsCheckedAlready, bool decrement = false)
{ {
@@ -104,18 +112,26 @@ public static class Car_Extensions
Rect rect = new Rect(new Vector2(center.x - 30f, center.z - 30f), Vector2.one * 30f * 2f); Rect rect = new Rect(new Vector2(center.x - 30f, center.z - 30f), Vector2.one * 30f * 2f);
var cars = tc.CarIdsInRect(rect); var cars = tc.CarIdsInRect(rect);
Log.Information($"{nameof(HuntingForCabeeseNearCar)} => {cars.Count()}"); Log.Information($"{nameof(HuntingForCabeeseNearCar)} => {cars.Count()}");
List<(string carId, float distance)> source = List<(string carId, float distance)> source =
cars cars
.Select(carId => .Select(carId =>
{
Car car = tc.CarForId(carId);
if (car == null || !car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready))
{ {
Car car = tc.CarForId(carId); return (carId: carId, distance: 1000f);
if (car == null || !car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready)) }
{ Vector3 a = WorldTransformer.WorldToGame(car.GetMotionSnapshot().Position);
return (carId: carId, distance: 1000f); return (carId: carId, distance: Vector3.Distance(a, center));
} }).ToList();
Vector3 a = WorldTransformer.WorldToGame(car.GetMotionSnapshot().Position);
return (carId: carId, distance: Vector3.Distance(a, center));
}).ToList();
return source; return source;
} }
public static void AdjustHotboxValue(this Car car, float hotboxValue) =>
StateManager.ApplyLocal(
new PropertyChange(
car.id, PropertyChange.KeyForControl(PropertyChange.Control.Hotbox),
new FloatPropertyValue(hotboxValue)
)
);
} }

View File

@@ -0,0 +1,27 @@
using HarmonyLib;
using Model.AI;
using Railloader;
using RMROC451.TweaksAndThings.Extensions;
using Serilog;
using System.Collections;
namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatch(typeof(AutoHotboxSpotter))]
[HarmonyPatch(nameof(AutoHotboxSpotter.SpotterLoop))]
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
internal class AutoHotboxSpotter_SpotterLoop_Patch
{
private static ILogger _log => Log.ForContext<AutoHotboxSpotter_SpotterLoop_Patch>();
public static bool Prefix(AutoHotboxSpotter __instance, ref IEnumerator __result)
{
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return true;
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
if (buttonsHaveCost) __result = __instance.MrocAutoHotboxSpotterLoop(_log, cabooseRequired);
return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
}
}

View File

@@ -0,0 +1,27 @@
using HarmonyLib;
using Model.AI;
using Railloader;
using RMROC451.TweaksAndThings.Extensions;
using Serilog;
using System.Collections;
namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatch(typeof(AutoOiler))]
[HarmonyPatch(nameof(AutoOiler.Loop))]
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
internal class AutoOiler_Loop_Patch
{
private static ILogger _log => Log.ForContext<AutoOiler_Loop_Patch>();
public static bool Prefix(AutoOiler __instance, ref IEnumerator __result)
{
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return true;
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
if (buttonsHaveCost) __result = __instance.MrocAutoOilerLoop(_log, cabooseRequired);
return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
}
}

View File

@@ -1,19 +1,22 @@
using Game.Messages; using Core;
using Game.Messages;
using Game.State; using Game.State;
using HarmonyLib; using HarmonyLib;
using KeyValue.Runtime; using KeyValue.Runtime;
using Model; using Model;
using Model.OpsNew;
using Network; using Network;
using Railloader; using Railloader;
using RMROC451.TweaksAndThings.Enums; using RMROC451.TweaksAndThings.Enums;
using RMROC451.TweaksAndThings.Extensions; using RMROC451.TweaksAndThings.Extensions;
using RollingStock;
using Serilog; using Serilog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UI;
using UI.Builder; using UI.Builder;
using UI.CarInspector; using UI.CarInspector;
using UI.ContextMenu;
using UI.Tags; using UI.Tags;
using static Model.Car; using static Model.Car;
@@ -24,6 +27,7 @@ namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatchCategory("RMROC451TweaksAndThings")] [HarmonyPatchCategory("RMROC451TweaksAndThings")]
internal class CarInspector_PopulateCarPanel_Patch internal class CarInspector_PopulateCarPanel_Patch
{ {
private static ILogger _log => Log.ForContext<CarInspector_PopulateCarPanel_Patch>();
private static IEnumerable<LogicalEnd> ends = Enum.GetValues(typeof(LogicalEnd)).Cast<LogicalEnd>(); private static IEnumerable<LogicalEnd> ends = Enum.GetValues(typeof(LogicalEnd)).Cast<LogicalEnd>();
/// <summary> /// <summary>
@@ -37,40 +41,65 @@ internal class CarInspector_PopulateCarPanel_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();
var consist = __instance._car.EnumerateCoupled(LogicalEnd.A); var consist = __instance._car.EnumerateCoupled();
builder = AddCarConsistRebuildObservers(builder, consist); builder = AddCarConsistRebuildObservers(builder, consist);
builder.HStack(delegate (UIPanelBuilder hstack) builder.HStack(delegate (UIPanelBuilder hstack)
{ {
var buttonName = $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} {TextSprites.HandbrakeWheel}"; 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); MrocConsistHelper(__instance._car, MrocHelperType.Handbrake, buttonsHaveCost);
hstack.Rebuild(); hstack.Rebuild();
}).Tooltip(buttonName, $"Iterates over cars in this consist and {(consist.Any(c => c.HandbrakeApplied()) ? "releases" : "sets")} {TextSprites.HandbrakeWheel}."); }).Tooltip(buttonName, $"Iterates over cars in this consist and {(consist.Any(c => c.HandbrakeApplied()) ? "releases" : "sets")} {TextSprites.HandbrakeWheel}.");
if (consist.Any(c => c.EndAirSystemIssue())) if (consist.Any(c => c.EndAirSystemIssue()))
{ {
hstack.AddButtonCompact("Connect Air", delegate { hstack.AddButtonCompact("Connect Air", delegate
{
MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost); MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
hstack.Rebuild(); hstack.Rebuild();
}).Tooltip("Connect Consist Air", "Iterates over each car in this consist and connects gladhands and opens anglecocks."); }).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); MrocConsistHelper(__instance._car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
hstack.Rebuild(); hstack.Rebuild();
}).Tooltip("Bleed Air Lines", "Iterates over each car in this consist and bleeds the air out of the lines."); }).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; 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) private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist)
{ {
TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>(); TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>();
foreach (Model.Car car in consist) foreach (Model.Car car in consist.Where(c => c.Archetype != Model.Definition.CarArchetype.Tender))
{ {
builder = AddObserver(builder, car, PropertyChange.KeyForControl(PropertyChange.Control.Handbrake), tagController); builder = AddObserver(builder, car, PropertyChange.KeyForControl(PropertyChange.Control.Handbrake), tagController);
foreach (LogicalEnd logicalEnd in ends) foreach (LogicalEnd logicalEnd in ends)
@@ -93,12 +122,13 @@ internal class CarInspector_PopulateCarPanel_Patch
{ {
try try
{ {
tagController.UpdateTag(car, car.TagCallout, OpsController.Shared); builder.RebuildOnInterval(.01f);
builder.Rebuild(); if (car.TagCallout != null) tagController.UpdateTags(CameraSelector.shared._currentCamera.GroundPosition, true);
if (ContextMenu.IsShown && ContextMenu.Shared.centerLabel.text == car.DisplayName) CarPickable.HandleShowContextMenu(car);
} }
catch(Exception ex) catch (Exception ex)
{ {
Log.Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}"); _log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
} }
}, },
false false
@@ -110,7 +140,7 @@ internal class CarInspector_PopulateCarPanel_Patch
//var dh = new DownloadHandlerAudioClip($"file://{cacheFileName}", AudioType.MPEG); //var dh = new DownloadHandlerAudioClip($"file://{cacheFileName}", AudioType.MPEG);
//dh.compressed = true; // This //dh.compressed = true; // This
//using (UnityWebRequest wr = new UnityWebRequest($"file://{cacheFileName}", "GET", dh, null)) { //using (UnityWebRequest wr = new UnityWebRequest($"file://{cacheFileName}", "GET", dh, null)) {
// yield return wr.SendWebRequest(); // yield return wr.SendWebRequest();
// if (wr.responseCode == 200) { // if (wr.responseCode == 200) {
@@ -121,8 +151,8 @@ internal class CarInspector_PopulateCarPanel_Patch
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost) public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost)
{ {
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>(); TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
IEnumerable<Model.Car> consist = car.EnumerateCoupled(LogicalEnd.A); IEnumerable<Model.Car> consist = car.EnumerateCoupled();
//Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}"); _log.ForContext("car", car).Verbose($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
CalculateCostIfEnabled(car, mrocHelperType, buttonsHaveCost, consist); CalculateCostIfEnabled(car, mrocHelperType, buttonsHaveCost, consist);
@@ -136,7 +166,6 @@ internal class CarInspector_PopulateCarPanel_Patch
else else
{ {
consist = consist.Where(c => c is not BaseLocomotive && c.Archetype != Model.Definition.CarArchetype.Tender); consist = consist.Where(c => c is not BaseLocomotive && c.Archetype != Model.Definition.CarArchetype.Tender);
Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
//when ApplyHandbrakesAsNeeded is called, and the consist contains an engine, it stops applying brakes. //when ApplyHandbrakesAsNeeded is called, and the consist contains an engine, it stops applying brakes.
tc.ApplyHandbrakesAsNeeded(consist.ToList(), PlaceTrainHandbrakes.Automatic); tc.ApplyHandbrakesAsNeeded(consist.ToList(), PlaceTrainHandbrakes.Automatic);
} }
@@ -163,7 +192,7 @@ internal class CarInspector_PopulateCarPanel_Patch
case MrocHelperType.BleedAirSystem: case MrocHelperType.BleedAirSystem:
consist = consist.Where(c => c.NotMotivePower()); consist = consist.Where(c => c.NotMotivePower());
Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}"); _log.ForContext("car", car).Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
foreach (Model.Car bleed in consist) foreach (Model.Car bleed in consist)
{ {
StateManager.ApplyLocal(new PropertyChange(bleed.id, PropertyChange.Control.Bleed, 1)); StateManager.ApplyLocal(new PropertyChange(bleed.id, PropertyChange.Control.Bleed, 1));
@@ -184,14 +213,14 @@ internal class CarInspector_PopulateCarPanel_Patch
if (cabooseWithAvailCrew == null) timeCost *= 1.5f; if (cabooseWithAvailCrew == null) timeCost *= 1.5f;
var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose"; var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose";
Log.Information($"{nameof(MrocConsistHelper)} {mrocHelperType} : [VACINITY CABEESE FOUND:{cabooseWithAvailCrew?.ToString() ?? "NONE"}] => Consist Length {consist.Count()} => costs {timeCost / 60} minutes of AI Engineer time, $5 per hour = ~${Math.Ceiling((decimal)(timeCost / 3600) * 5)} (*2 if no caboose nearby)"); _log.ForContext("car", car).Information($"{nameof(MrocConsistHelper)} {mrocHelperType} : [VACINITY CABEESE FOUND:{cabooseWithAvailCrew?.ToString() ?? "NONE"}] => Consist Length {consist.Count()} => costs {timeCost / 60} minutes of AI Engineer time, $5 per hour = ~${Math.Ceiling((decimal)(timeCost / 3600) * 5)} (*2 if no caboose nearby)");
Multiplayer.SendError(StateManager.Shared._playersManager.LocalPlayer, $"{(cabooseWithAvailCrew != null ? $"{cabooseWithAvailCrew.DisplayName} Hours Adjusted: ({tsString})\n" : string.Empty)}Wages: ~(${Math.Ceiling((decimal)(timeCost / 3600) * 5)})"); Multiplayer.SendError(StateManager.Shared._playersManager.LocalPlayer, $"{(cabooseWithAvailCrew != null ? $"{cabooseWithAvailCrew.DisplayName} Hours Adjusted: ({tsString})\n" : string.Empty)}Wages: ~(${Math.Ceiling((decimal)(timeCost / 3600) * 5)})");
if (buttonsHaveCost) StateManager_OnDayDidChange_Patch.UnbilledAutoBrakeCrewRunDuration += timeCost; StateManager_OnDayDidChange_Patch.UnbilledAutoBrakeCrewRunDuration += timeCost;
} }
} }
public static Car? NearbyCabooseWithAvailableCrew(Car car, float timeNeeded, bool decrement = false) public static Car? NearbyCabooseWithAvailableCrew(Car car, float timeNeeded, bool decrement = false)
{ {
@@ -203,7 +232,7 @@ internal class CarInspector_PopulateCarPanel_Patch
carIdsCheckedAlready.Add(car.id); carIdsCheckedAlready.Add(car.id);
//check consist, for cabeese //check consist, for cabeese
IEnumerable<Car> consist = car.EnumerateCoupled(LogicalEnd.A); IEnumerable<Car> consist = car.EnumerateCoupled();
output = consist.FirstOrDefault(c => c.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement)); output = consist.FirstOrDefault(c => c.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement));
if (output != null) return output; //short out if we are good if (output != null) return output; //short out if we are good
carIdsCheckedAlready.UnionWith(consist.Select(c => c.id)); carIdsCheckedAlready.UnionWith(consist.Select(c => c.id));

View File

@@ -0,0 +1,52 @@
using HarmonyLib;
using Railloader;
using RollingStock;
using UnityEngine;
namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatch(typeof(CarPickable))]
[HarmonyPatch(nameof(CarPickable.Activate), typeof(PickableActivateEvent))]
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
internal class CarPickable_Activate_Patch
{
static float clicked = 0;
static float clicktime = 0;
static float clickdelay = 0.5f;
private static bool Prefix(CarPickable __instance, PickableActivateEvent evt)
{
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return true;
if (OnPointerDown(evt))
{
CameraSelector.shared.FollowCar(__instance.car);
return false;
}
return true;
}
public static bool OnPointerDown(PickableActivateEvent evt)
{
bool output = false;
if (evt.Activation == PickableActivation.Primary)
{
clicked++;
if (clicked == 1) clicktime = Time.time;
if (clicked > 1 && Time.time - clicktime < clickdelay)
{
clicked = 0;
clicktime = 0;
output = true;
}
else if (clicked > 2 || Time.time - clicktime > 1) clicked = 0;
}
return output;
}
}

View File

@@ -0,0 +1,54 @@
using HarmonyLib;
using Model;
using Railloader;
using RMROC451.TweaksAndThings.Enums;
using RMROC451.TweaksAndThings.Extensions;
using RollingStock;
using System.Linq;
using UI;
using UI.ContextMenu;
namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatch(typeof(CarPickable))]
[HarmonyPatch(nameof(CarPickable.HandleShowContextMenu), typeof(Car))]
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
internal class CarPickable_HandleShowContextMenu_Patch
{
private static void Postfix(Car car)
{
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled) return;
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
ContextMenu shared = ContextMenu.Shared;
shared.AddButton(ContextMenuQuadrant.Unused2, $"{(car.EnumerateCoupled().Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate
{
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost);
});
if (car.EnumerateCoupled().Any(c => c.EndAirSystemIssue()))
{
shared.AddButton(ContextMenuQuadrant.Unused2, $"Air Up Consist", SpriteName.Select, delegate
{
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
});
}
if (car.EnumerateCoupled().Any(c => c.SupportsBleed()))
{
shared.AddButton(ContextMenuQuadrant.Unused2, $"Bleed Consist", SpriteName.Bleed, delegate
{
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
});
}
shared.AddButton(ContextMenuQuadrant.Unused2, $"Follow", SpriteName.Inspect, delegate
{
CameraSelector.shared.FollowCar(car);
});
shared.BuildItemAngles();
shared.StartCoroutine(shared.AnimateButtonsShown());
}
}

View File

@@ -3,9 +3,9 @@ using Model;
using Model.OpsNew; using Model.OpsNew;
using Railloader; using Railloader;
using RMROC451.TweaksAndThings.Extensions; using RMROC451.TweaksAndThings.Extensions;
using UI; using System.Collections.Generic;
using System.Linq;
using UI.Tags; using UI.Tags;
using UnityEngine;
namespace RMROC451.TweaksAndThings.Patches; namespace RMROC451.TweaksAndThings.Patches;
@@ -19,7 +19,6 @@ internal class TagController_UpdateTag_Patch
private static void Postfix(Car car, TagCallout tagCallout) private static void Postfix(Car car, TagCallout tagCallout)
{ {
TagController tagController = UnityEngine.Object.FindObjectOfType<TagController>();
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared; TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers) if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
@@ -27,31 +26,24 @@ internal class TagController_UpdateTag_Patch
return; return;
} }
ProceedWithPostFix(car, tagCallout, tagController); ProceedWithPostFix(car, tagCallout);
return; return;
} }
private static void ProceedWithPostFix(Car car, TagCallout tagCallout, TagController tagController) private static void ProceedWithPostFix(Car car, TagCallout tagCallout)
{ {
bool isAltDownWithCarIssue = GameInput.IsAltDown && car.CarOrEndGearIssue();
tagCallout.callout.Title = string.Format(tagTitleFormat, "{0}", car.DisplayName); tagCallout.callout.Title = string.Format(tagTitleFormat, "{0}", car.DisplayName);
tagCallout.gameObject.SetActive( List<string> tags = [];
tagCallout.gameObject.activeSelf &&
(!GameInput.IsAltDown || isAltDownWithCarIssue)
);
if (tagCallout.gameObject.activeSelf && isAltDownWithCarIssue) if (car.HasHotbox) tags.Add(TextSprites.Hotbox);
{ if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
tagController.ApplyImageColor(tagCallout, Color.black); if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
}
tagCallout.callout.Title = 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}{string.Join("", tags)}".Replace("{0}", tags.Count().ToString()),
(_, true, _) => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{TextSprites.CycleWaybills}".Replace("{0}", "1"),
(_, _, true) => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{TextSprites.HandbrakeWheel}".Replace("{0}", "1"),
_ => car.DisplayName _ => car.DisplayName
}; };
} }

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,13 +92,22 @@ 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) =>
input.EndGearHelpersRequirePayment() && car.set.Cars.CabooseInConsist() && car.NotMotivePower();
} }

View File

@@ -82,16 +82,78 @@ 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 static string cabooseUse => "Caboose Use";
private static string autoAiRequirment => "AutoAI\nRequirement";
private void CabooseMods(UIPanelBuilder builder)
{
builder.AddField(
cabooseUse,
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).
AutoOiler Update: if `{cabooseUse}` & `{autoAiRequirment.Replace("\n", " ")}` checked, then when a caboose is present, the AutoOiler will repair hotboxes afer oiling them to 100%.
AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - 30 seconds (Safety Is Everyone's Job)");
builder.AddField(
autoAiRequirment,
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 +185,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"
}
]
}
}