mirror of
https://github.com/rmroc451/TweaksAndThings.git
synced 2025-12-16 09:19:37 -06:00
Compare commits
17 Commits
V2.1.0-bet
...
v2.1.7-bet
| Author | SHA1 | Date | |
|---|---|---|---|
| b911ee6f6f | |||
| cc18579507 | |||
| 1dadb04cbe | |||
| 1032edb7ef | |||
| c0e75c3c39 | |||
| bd215d0dcc | |||
| 554ea11790 | |||
| c114aec3b3 | |||
| 38a43276f7 | |||
| afd18b9d27 | |||
| 52e9fe1cab | |||
| ec9aadd6e2 | |||
| af8e1bd6ca | |||
| a9943b6c0c | |||
| 2713247895 | |||
| 22c6fb8e5e | |||
| fa7e7dfcd2 |
@@ -2,6 +2,6 @@
|
||||
<PropertyGroup>
|
||||
<MajorVersion>2</MajorVersion>
|
||||
<MinorVersion>1</MinorVersion>
|
||||
<PatchVersion>0</PatchVersion>
|
||||
<PatchVersion>7</PatchVersion>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -2,6 +2,7 @@
|
||||
using Game.State;
|
||||
using Helpers;
|
||||
using Model.Ops;
|
||||
using Model.Ops.Timetable;
|
||||
using Network;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RMROC451.TweaksAndThings.Patches;
|
||||
@@ -46,9 +47,16 @@ public class EchoCommand : IConsoleCommand
|
||||
EntityReference loco = new EntityReference(EntityType.Car, car.id);
|
||||
if (comps[2] == "+") message = new Hyperlink(entityReference.URI(), string.Format(message, OpsController.Shared.ClosestArea(car)?.name ?? "???"));
|
||||
|
||||
if (StateManager.IsHost) car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}");
|
||||
ExpandedConsole_Add_Patch.SendMs(null, $"{Hyperlink.To(car)} {message}");
|
||||
if (!StateManager.IsHost) Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\"");
|
||||
string hlt = Hyperlink.To(car);
|
||||
hlt = car.TryGetTimetableTrain(out Timetable.Train t) ? hlt.Replace(car.DisplayName, t.DisplayStringLong) : hlt;
|
||||
|
||||
if (StateManager.IsHost)
|
||||
{
|
||||
car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}");
|
||||
|
||||
ExpandedConsole_Add_Patch.SendMs(null, $"{StateManager.Shared._playersManager.LocalPlayer} {hlt} {message}");
|
||||
}
|
||||
if (!StateManager.IsHost) Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {hlt}: \"{message}\"");
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
"mixintos": {
|
||||
"container:ne-caboose01": "file(mroc-cabeese.json)",
|
||||
"container:ne-caboose02": "file(mroc-cabeese.json)",
|
||||
"container:ne-caboose03": "file(mroc-cabeese.json)",
|
||||
"game-graph": "file(tweakedLoadIDS.json)"
|
||||
"container:ne-caboose03": "file(mroc-cabeese.json)"
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace RMROC451.TweaksAndThings.Extensions
|
||||
public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log, bool cabooseRequired)
|
||||
{
|
||||
int originIndex = oiler.FindOriginIndex();
|
||||
Model.Car? foundCaboose = oiler._originCar.FindMyCaboose(0.0f, false);
|
||||
Model.Car? foundCaboose = oiler._originCar.FindMyCabooseSansLoadRequirement();
|
||||
if (originIndex < 0)
|
||||
{
|
||||
_log.Error("Couldn't find origin car {car}", oiler._originCar);
|
||||
@@ -41,7 +41,7 @@ namespace RMROC451.TweaksAndThings.Extensions
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(AutoOiler.StartDelay.CabooseHalvedFloat(foundCaboose));
|
||||
foundCaboose = oiler._originCar.FindMyCaboose(0.0f,false);
|
||||
foundCaboose = oiler._originCar.FindMyCabooseSansLoadRequirement();
|
||||
int carIndex = originIndex;
|
||||
float adjustedTimeToWalk = AutoOiler.TimeToWalkCar.CabooseHalvedFloat(foundCaboose);
|
||||
do
|
||||
@@ -80,7 +80,7 @@ namespace RMROC451.TweaksAndThings.Extensions
|
||||
|
||||
public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log, bool cabooseRequired)
|
||||
{
|
||||
Func<Model.Car?> foundCaboose = () => spotter._locomotive.FindMyCaboose(0.0f, false);
|
||||
Func<Model.Car?> foundCaboose = () => spotter._locomotive.FindMyCabooseSansLoadRequirement();
|
||||
while (true)
|
||||
{
|
||||
if (!spotter.HasCars)
|
||||
|
||||
@@ -7,6 +7,7 @@ using Model.Definition.Data;
|
||||
using Model.Ops;
|
||||
using Model.Ops.Timetable;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Patches;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -98,21 +99,28 @@ public static class Car_Extensions
|
||||
input.SelectedLocomotive.TryGetTimetableTrain(out Timetable.Train t) &&
|
||||
t.TrainClass == Timetable.TrainClass.First;
|
||||
|
||||
public static Car? FindMyCaboose(this Car car, float timeNeeded, bool decrement = false) =>
|
||||
public static Car? FindMyCabooseSansLoadRequirement(this Car car) =>
|
||||
FindMyCaboose(car, 0f, decrement: false, requireLoad: false);
|
||||
|
||||
public static Car? FindMyCabooseWithLoadRequirement(this Car car, float timeNeeded, bool decrement) =>
|
||||
FindMyCaboose(car, timeNeeded, decrement, requireLoad: true);
|
||||
|
||||
private static Car? FindMyCaboose(this Car car, float timeNeeded, bool decrement = false, bool requireLoad = true) =>
|
||||
(
|
||||
car.CarCaboose() ?? car.CarsNearCurrentCar(timeNeeded, decrement).FindNearestCabooseFromNearbyCars()
|
||||
)?.CabooseWithSufficientCrewHours(timeNeeded, decrement);
|
||||
)?.CabooseWithSufficientCrewHours(timeNeeded: timeNeeded, requireLoad:requireLoad, decrement: decrement);
|
||||
|
||||
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, bool decrement = false)
|
||||
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, bool requireLoad, bool decrement = false)
|
||||
{
|
||||
Car? output = null;
|
||||
if (car is null || !car.IsCaboose()) return null;
|
||||
if (!requireLoad) return car;
|
||||
|
||||
List<LoadSlot> loadSlots = car.Definition.LoadSlots;
|
||||
for (int i = 0; i < loadSlots.Count; i++)
|
||||
{
|
||||
CarLoadInfo? loadInfo = car.GetLoadInfo(i);
|
||||
if (loadInfo.HasValue)
|
||||
if (loadInfo.HasValue && loadInfo.Value.LoadId == OpsController_AnnounceCoalescedPayments_Patch.CrewLoadHours.id)
|
||||
{
|
||||
CarLoadInfo valueOrDefault = loadInfo.GetValueOrDefault();
|
||||
output = valueOrDefault.Quantity >= timeNeeded ? car : null;
|
||||
|
||||
@@ -13,7 +13,7 @@ internal class AutoEngineerControlSetBase_UpdateStatusLabel_Patch
|
||||
static void Postfix(AutoEngineerControlSetBase __instance)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled() || !AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.SafetyFirstGoverningApplies()) return;
|
||||
if (!tweaksAndThings.IsEnabled() || !AutoEngineerPlanner_HandleCommand_Patch.SafetyFirstGoverningApplies(__instance.Locomotive)) return;
|
||||
|
||||
string orig = __instance.statusLabel.text;
|
||||
__instance.statusLabel.text = $"{orig}; <b>Safety</b>";
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
using Game.Messages;
|
||||
using HarmonyLib;
|
||||
using Model.AI;
|
||||
using Model.Definition;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UI.EngineControls;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
|
||||
[HarmonyPatch(typeof(AutoEngineerOrdersHelper))]
|
||||
[HarmonyPatch(nameof(AutoEngineerOrdersHelper.SendAutoEngineerCommand), typeof(AutoEngineerMode), typeof(bool), typeof(int), typeof(float), typeof(OrderWaypoint))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch
|
||||
{
|
||||
|
||||
private static Serilog.ILogger _log => Log.ForContext<AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch>();
|
||||
|
||||
static bool Prefix(AutoEngineerMode mode, ref int maxSpeedMph)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled() || !tweaksAndThings.SafetyFirst()) return true;
|
||||
|
||||
if (SafetyFirstGoverningApplies())
|
||||
{
|
||||
int orig = maxSpeedMph;
|
||||
int limitedSpeed = Math.Min(maxSpeedMph, 20);
|
||||
maxSpeedMph = mode switch
|
||||
{
|
||||
AutoEngineerMode.Road => limitedSpeed,
|
||||
AutoEngineerMode.Waypoint => limitedSpeed,
|
||||
_ => maxSpeedMph,
|
||||
};
|
||||
|
||||
if (orig != maxSpeedMph)
|
||||
{
|
||||
_log.Debug($"{Enum.GetName(typeof(AutoEngineerMode), mode)}[{TrainController.Shared.SelectedLocomotive.DisplayName}] {nameof(AutoEngineerOrdersExtensions.MaxSpeedMph)} limited to {limitedSpeed} from {orig}; No Caboose in Consist;");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
internal static bool SafetyFirstGoverningApplies()
|
||||
{
|
||||
var _persistence = new AutoEngineerPersistence(TrainController.Shared.SelectedLocomotive.KeyValueObject);
|
||||
var OrdersHelper = new AutoEngineerOrdersHelper(TrainController.Shared.SelectedLocomotive, _persistence);
|
||||
|
||||
if (TrainController.Shared.SelectedLocomotive.EnumerateCoupled().All(c => c.IsCaboose() || c.MotivePower())) return false;
|
||||
|
||||
bool cabooseReq = SingletonPluginBase<TweaksAndThingsPlugin>.Shared.RequireConsistCabooseForOilerAndHotboxSpotter();
|
||||
string logMessage = $"\n{nameof(SafetyFirstGoverningApplies)}:{Enum.GetName(typeof(AutoEngineerMode), OrdersHelper.Mode)}[{TrainController.Shared.SelectedLocomotive.DisplayName}] ";
|
||||
Func<bool> firstClass = () =>
|
||||
{
|
||||
var output = TrainController.Shared.SelectedEngineExpress();
|
||||
logMessage += $"\nfirst class {output}";
|
||||
return output;
|
||||
};
|
||||
|
||||
Func<bool> FreightConsist = () =>
|
||||
{
|
||||
bool output = !TrainController.Shared.SelectedLocomotive.EnumerateCoupled().ConsistNoFreight();
|
||||
logMessage += $"\nFreightConsist? {output}";
|
||||
logMessage += " " + string.Join(" / ", TrainController.Shared.SelectedLocomotive.EnumerateCoupled().Where(c => !c.MotivePower()).Select(c => $"{c.id} {Enum.GetName(typeof(CarArchetype), c.Archetype)}"));
|
||||
return output;
|
||||
};
|
||||
|
||||
Func<bool> noCaboose = () =>
|
||||
{
|
||||
var output = TrainController.Shared.SelectedLocomotive.FindMyCaboose(0.0f, false) == null;
|
||||
logMessage += $"\ncaboose? {!output}";
|
||||
return output;
|
||||
};
|
||||
|
||||
logMessage += $"\nCaboose Required {cabooseReq}";
|
||||
|
||||
bool output =
|
||||
cabooseReq &&
|
||||
!firstClass() &&
|
||||
FreightConsist() &&
|
||||
noCaboose();
|
||||
|
||||
logMessage += $"\nGovern AE? {output}";
|
||||
|
||||
_log.Debug(logMessage);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using Game;
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using Model;
|
||||
using Model.AI;
|
||||
using Model.Definition;
|
||||
using Network;
|
||||
using Network.Messages;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UI.EngineControls;
|
||||
using UI.EngineRoster;
|
||||
using static Unity.IO.LowLevel.Unsafe.AsyncReadManagerMetrics;
|
||||
using static UnityEngine.InputSystem.InputRemoting;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(AutoEngineerPlanner))]
|
||||
[HarmonyPatch(nameof(AutoEngineerPlanner.HandleCommand))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class AutoEngineerPlanner_HandleCommand_Patch
|
||||
{
|
||||
private static Serilog.ILogger _log => Log.ForContext<AutoEngineerPlanner_HandleCommand_Patch>();
|
||||
private static int governedSpeed = 20;
|
||||
|
||||
static bool Prefix(AutoEngineerPlanner __instance, ref AutoEngineerCommand command, ref IPlayer sender)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled() || !tweaksAndThings.SafetyFirst() || (sender.IsRemote && !tweaksAndThings.SafetyFirstClientEnforce()) || command.MaxSpeedMph <= governedSpeed) return true;
|
||||
BaseLocomotive loco = TrainController.Shared.SelectedLocomotive;
|
||||
if (TrainController.Shared.TryGetCarForId(command.LocomotiveId, out Car c)) loco = (BaseLocomotive)c;
|
||||
|
||||
if (SafetyFirstGoverningApplies(loco))
|
||||
{
|
||||
int orig = command.MaxSpeedMph;
|
||||
int limitedSpeed = Math.Min(command.MaxSpeedMph, governedSpeed);
|
||||
command.MaxSpeedMph = command.Mode switch
|
||||
{
|
||||
AutoEngineerMode.Road => limitedSpeed,
|
||||
AutoEngineerMode.Waypoint => limitedSpeed,
|
||||
_ => command.MaxSpeedMph,
|
||||
};
|
||||
|
||||
string message = $"{Enum.GetName(typeof(AutoEngineerMode), command.Mode)}[{loco.DisplayName}] governed{{0}}due to Safety First rules.";
|
||||
if (orig != command.MaxSpeedMph)
|
||||
{
|
||||
message = string.Format(message, $" from {orig} to {command.MaxSpeedMph} MPH ");
|
||||
}else
|
||||
{
|
||||
message = string.Format(message, " ");
|
||||
}
|
||||
_log.Debug(message);
|
||||
Multiplayer.SendError(sender, message, AlertLevel.Info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool SafetyFirstGoverningApplies(BaseLocomotive loco)
|
||||
{
|
||||
var _persistence = new AutoEngineerPersistence(loco.KeyValueObject);
|
||||
var OrdersHelper = new AutoEngineerOrdersHelper(loco, _persistence);
|
||||
|
||||
if (loco.EnumerateCoupled().All(c => c.IsCaboose() || c.MotivePower())) return false;
|
||||
|
||||
bool cabooseReq = SingletonPluginBase<TweaksAndThingsPlugin>.Shared.RequireConsistCabooseForOilerAndHotboxSpotter();
|
||||
string logMessage = $"\n{nameof(SafetyFirstGoverningApplies)}:{Enum.GetName(typeof(AutoEngineerMode), OrdersHelper.Mode)}[{loco.DisplayName}] ";
|
||||
Func<bool> firstClass = () =>
|
||||
{
|
||||
var output = TrainController.Shared.SelectedEngineExpress();
|
||||
logMessage += $"\nfirst class {output}";
|
||||
return output;
|
||||
};
|
||||
|
||||
Func<bool> FreightConsist = () =>
|
||||
{
|
||||
bool output = !loco.EnumerateCoupled().ConsistNoFreight();
|
||||
logMessage += $"\nFreightConsist? {output}";
|
||||
logMessage += " " + string.Join(" / ", loco.EnumerateCoupled().Where(c => !c.MotivePower()).Select(c => $"{c.id} {Enum.GetName(typeof(CarArchetype), c.Archetype)}"));
|
||||
return output;
|
||||
};
|
||||
|
||||
Func<bool> noCaboose = () =>
|
||||
{
|
||||
bool output = loco.FindMyCabooseSansLoadRequirement() == null;
|
||||
logMessage += $"\ncaboose? {!output}";
|
||||
return output;
|
||||
};
|
||||
|
||||
logMessage += $"\nCaboose Required {cabooseReq}";
|
||||
|
||||
bool output =
|
||||
cabooseReq &&
|
||||
!firstClass() &&
|
||||
FreightConsist() &&
|
||||
noCaboose();
|
||||
|
||||
logMessage += $"\nGovern AE? {output}";
|
||||
if (_log.IsEnabled(Serilog.Events.LogEventLevel.Verbose))
|
||||
logMessage += $"\n{Environment.StackTrace}";
|
||||
|
||||
_log.Debug(logMessage);
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
using Game.State;
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using KeyValue.Runtime;
|
||||
using Model;
|
||||
using Model.AI;
|
||||
using Model.Ops;
|
||||
using Model.Ops.Timetable;
|
||||
using Network;
|
||||
using Network.Messages;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Track;
|
||||
@@ -16,97 +18,123 @@ using Track.Search;
|
||||
using UI;
|
||||
using UI.EngineControls;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using static Track.Search.RouteSearch;
|
||||
using Location = Track.Location;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(AutoEngineerWaypointControls))]
|
||||
[HarmonyPatch(nameof(AutoEngineerWaypointControls.ConfigureOptionsDropdown))]
|
||||
[HarmonyPatch(typeof(LocomotiveControlsUIAdapter))]
|
||||
[HarmonyPatch(nameof(LocomotiveControlsUIAdapter.UpdateCarText))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
internal class LocomotiveControlsUIAdapter_UpdateCarText_Postfix()
|
||||
{
|
||||
private static Serilog.ILogger _log => Log.ForContext<AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch>();
|
||||
private static Serilog.ILogger _log => Log.ForContext<LocomotiveControlsUIAdapter_UpdateCarText_Postfix>();
|
||||
private static int lastSeenIntegrationSetCount = default;
|
||||
private static string? lastLocoSeenCarId = default;
|
||||
private static Coroutine? watchyWatchy = null;
|
||||
private static HashSet<OpsCarPosition?> locoConsistDestinations = [];
|
||||
private static Game.GameDateTime? timetableSaveTime = null;
|
||||
static string getDictKey(Car car) => car.DisplayName;
|
||||
|
||||
private static readonly HashSet<IDisposable> _keyChangeObservers = new HashSet<IDisposable>();
|
||||
private static readonly HashSet<string> destinations = new HashSet<string>();
|
||||
private static readonly HashSet<Car> consist = new HashSet<Car>();
|
||||
private static string _lastLocoCarId = string.Empty;
|
||||
private static bool recalcing = false;
|
||||
|
||||
static void Postfix(AutoEngineerWaypointControls __instance, ref OptionsDropdownConfiguration __result)
|
||||
static void Postfix(LocomotiveControlsUIAdapter __instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
_log.Information($"HI BOB");
|
||||
foreach(var o in _keyChangeObservers) o.Dispose();
|
||||
_keyChangeObservers.Clear();
|
||||
destinations.Clear();
|
||||
recalcing = false;
|
||||
if (lastLocoSeenCarId != null && lastLocoSeenCarId.Equals(TrainController.Shared?.SelectedLocomotive.id) && watchyWatchy != null) return;
|
||||
if (watchyWatchy != null) ((MonoBehaviour)__instance).StopCoroutine(watchyWatchy);
|
||||
watchyWatchy = null;
|
||||
|
||||
if (__instance._persistence.Orders.Mode == AutoEngineerMode.Waypoint) watchyWatchy = ((MonoBehaviour)__instance).StartCoroutine(UpdateCogCoroutine(__instance));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(ex, "I have a very unique set of skills; I will find you and I will squash you.");
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator UpdateCogCoroutine(LocomotiveControlsUIAdapter __instance)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled()) return;
|
||||
//_log.Information($"HI2");
|
||||
WaitForSecondsRealtime wait = new WaitForSecondsRealtime(3f);
|
||||
|
||||
|
||||
List<DropdownMenu.RowData> rowDatas = __result.Rows.ToList();
|
||||
var func = __result.OnRowSelected;
|
||||
int origCount = rowDatas.Count;
|
||||
int maxRowOrig = origCount - 1;
|
||||
|
||||
var selectedLoco = TrainController.Shared.SelectedLocomotive;
|
||||
if (selectedLoco.id != _lastLocoCarId || !(consist?.Any() ?? false))
|
||||
while (true)
|
||||
{
|
||||
_lastLocoCarId = selectedLoco.id;
|
||||
consist.Clear();
|
||||
consist.UnionWith(selectedLoco.EnumerateCoupled()?.ToList() ?? Enumerable.Empty<Car>());
|
||||
if (__instance._persistence.Orders.Mode != AutoEngineerMode.Waypoint || ((AutoEngineerWaypointControls)__instance.aiWaypointControls).Locomotive == null) yield return wait;
|
||||
|
||||
PrepLocoUsage((AutoEngineerWaypointControls)__instance.aiWaypointControls, out BaseLocomotive selectedLoco, out int numberOfCars);
|
||||
HashSet<OpsCarPosition?> destinations = [];
|
||||
if (!tweaksAndThings.IsEnabled() || !ShouldRecalc(__instance, selectedLoco, out destinations)) yield return wait;
|
||||
timetableSaveTime = TimetableController.Shared.CurrentDocument.Modified;
|
||||
lastSeenIntegrationSetCount = selectedLoco.set.NumberOfCars;
|
||||
|
||||
IterateCarsDetectDestinations(
|
||||
(AutoEngineerWaypointControls)__instance.aiWaypointControls,
|
||||
((AutoEngineerWaypointControls)__instance.aiWaypointControls).ConfigureOptionsDropdown(),
|
||||
selectedLoco,
|
||||
numberOfCars,
|
||||
destinations: destinations,
|
||||
out List<DropdownMenu.RowData> rowDatas,
|
||||
out Action<int> func,
|
||||
out int origCount,
|
||||
out int maxRowOrig,
|
||||
out AutoEngineerOrdersHelper aeoh
|
||||
);
|
||||
|
||||
List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> jumpTos =
|
||||
BuildJumpToOptions((AutoEngineerWaypointControls)__instance.aiWaypointControls, selectedLoco);
|
||||
|
||||
var config = WireUpJumpTosToSettingMenu(
|
||||
(AutoEngineerWaypointControls)__instance.aiWaypointControls,
|
||||
selectedLoco,
|
||||
rowDatas,
|
||||
func,
|
||||
origCount,
|
||||
maxRowOrig,
|
||||
aeoh,
|
||||
ref jumpTos
|
||||
);
|
||||
|
||||
List<DropdownMenu.RowData> list = config.Rows;
|
||||
Action<int> action = config.OnRowSelected;
|
||||
|
||||
__instance.optionsDropdown.Configure(list, action);
|
||||
((Selectable)__instance.optionsDropdown).interactable = list.Count > 0;
|
||||
yield return wait;
|
||||
}
|
||||
var aeoh = new AutoEngineerOrdersHelper(persistence: new AutoEngineerPersistence(selectedLoco.KeyValueObject), locomotive: selectedLoco);
|
||||
List<(string destinationId, string destination, float? distance, Location? location)> jumpTos = new();
|
||||
}
|
||||
|
||||
foreach(var c in consist)
|
||||
{
|
||||
AddObserversToCar(__instance, c);
|
||||
OpsCarPosition? destination = c.Waybill.HasValue && !c.Waybill.Value.Completed ? c.Waybill.Value.Destination : null;
|
||||
bool completed = c.Waybill?.Completed ?? false;
|
||||
if (!destination.HasValue && c.TryGetOverrideDestination(OverrideDestination.Repair, OpsController.Shared, out (OpsCarPosition, string)? result)) destination = result.Value.Item1;
|
||||
_log.Information($"{c.DisplayName} -> {destination.HasValue}");
|
||||
private static bool ShouldRecalc(LocomotiveControlsUIAdapter __instance, BaseLocomotive selectedLoco, out HashSet<OpsCarPosition?> destinations)
|
||||
{
|
||||
bool output = false;
|
||||
string locoKey = getDictKey(selectedLoco);
|
||||
List<Car> consist = new List<Car>();
|
||||
consist = selectedLoco.EnumerateCoupled().ToList();
|
||||
destinations = consist.Where(c => GetCarDestinationIdentifier(c).HasValue).Select(GetCarDestinationIdentifier).ToHashSet();
|
||||
|
||||
string destId = destination?.Identifier ?? string.Empty;
|
||||
//_log.Information($"{locoKey} --> [{destinations.Count}] -> Seen -> {string.Join(Environment.NewLine, destinations.Select(k => k.Value.DisplayName))}");
|
||||
//_log.Information($"{locoKey} --> [{locoConsistDestinations.Count}] -> Cache -> {string.Join(Environment.NewLine, locoConsistDestinations.Select(k => $"{locoKey}:{k.Value.DisplayName}"))}");
|
||||
|
||||
if (destinations.Contains(destId)) continue;
|
||||
output |= !locoConsistDestinations.SetEquals(destinations);
|
||||
//_log.Information($"{locoKey} 1-> {output}");
|
||||
if (output) lastSeenIntegrationSetCount = default;
|
||||
output |= lastSeenIntegrationSetCount != selectedLoco.set.NumberOfCars;
|
||||
//_log.Information($"{locoKey} 2-> {output}");
|
||||
//output |= __instance.optionsDropdown.scrollRect.content.childCount != (destinations.Count + timetableDestinations.Count + 1); //+1 for the default "JumpTo" entry)
|
||||
//_log.Information($"{locoKey} 2.5-> {output} {__instance.optionsDropdown.scrollRect.content.childCount} {(destinations.Count)} {timetableDestinations.Count}");
|
||||
output |= selectedLoco.TryGetTimetableTrain(out _) && TimetableController.Shared.CurrentDocument.Modified != timetableSaveTime;
|
||||
//_log.Information($"{locoKey} 3-> {output}");
|
||||
|
||||
if (destination.HasValue && !completed)
|
||||
{
|
||||
string destName = destination.Value.DisplayName;
|
||||
float? distance = null;
|
||||
return output;
|
||||
}
|
||||
|
||||
if (Graph.Shared.TryGetLocationFromPoint(destination.Value.Spans?.FirstOrDefault().GetSegments().FirstOrDefault(), destination.Value.Spans?.FirstOrDefault()?.GetCenterPoint() ?? default, 200f, out Location destLoc))
|
||||
{
|
||||
|
||||
float trainMomentum = 0f;
|
||||
Location start = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.RouteStartLocation(out trainMomentum) : RouteStartLocation(__instance, selectedLoco);
|
||||
HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer;
|
||||
List<RouteSearch.Step> list = new List<RouteSearch.Step>();
|
||||
var totLen = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.CalculateTotalLength() : CalculateTotalLength(selectedLoco);
|
||||
distance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum)
|
||||
? metrics.Distance
|
||||
: null;
|
||||
};
|
||||
_log.Information($"{c.DisplayName} -> {destName} {destId} {distance?.ToString()}");
|
||||
if (distance.HasValue)
|
||||
{
|
||||
destinations.Add(destId);
|
||||
jumpTos.Add((
|
||||
destinationId: destId,
|
||||
destination: $"WP> {destName}"
|
||||
, distance: distance
|
||||
, location: (Location?)destLoc
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
jumpTos = jumpTos?.OrderBy(c => c.distance)?.ToList() ?? [];
|
||||
var safetyFirst = AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.SafetyFirstGoverningApplies() && jumpTos.Any();
|
||||
private static OptionsDropdownConfiguration WireUpJumpTosToSettingMenu(AutoEngineerWaypointControls __instance, BaseLocomotive selectedLoco, List<DropdownMenu.RowData> rowDatas, Action<int> func, int origCount, int maxRowOrig, AutoEngineerOrdersHelper aeoh, ref List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> jumpTos)
|
||||
{
|
||||
OptionsDropdownConfiguration __result;
|
||||
jumpTos = jumpTos?.OrderBy(c => c.sortDistance)?.ToList() ?? default;
|
||||
var localJumpTos = jumpTos.ToList();
|
||||
var safetyFirst = AutoEngineerPlanner_HandleCommand_Patch.SafetyFirstGoverningApplies(selectedLoco) && jumpTos.Any();
|
||||
|
||||
rowDatas.AddRange(jumpTos.Select(j =>
|
||||
new DropdownMenu.RowData(
|
||||
@@ -119,12 +147,12 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
rowDatas
|
||||
, delegate (int row)
|
||||
{
|
||||
_log.Information($"{TrainController.Shared.SelectedLocomotive.DisplayName} row {row}/{jumpTos.Count}/{rowDatas.Count}");
|
||||
_log.Debug($"{__instance.Locomotive.DisplayName} row {row}/{localJumpTos.Count}/{rowDatas.Count}");
|
||||
if (row <= maxRowOrig)
|
||||
{
|
||||
func(row);
|
||||
}
|
||||
if (row > maxRowOrig && jumpTos[row - origCount].location.HasValue)
|
||||
if (row > maxRowOrig && localJumpTos[row - origCount].location.HasValue)
|
||||
{
|
||||
if (safetyFirst)
|
||||
{
|
||||
@@ -132,7 +160,7 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
return;
|
||||
}
|
||||
float trainMomentum = 0f;
|
||||
Location end = jumpTos[row - origCount].location.Value;
|
||||
Location end = localJumpTos[row - origCount].location.Value;
|
||||
Location start = RouteStartLocation(__instance, selectedLoco);
|
||||
HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer;
|
||||
List<RouteSearch.Step> list = new List<RouteSearch.Step>();
|
||||
@@ -142,8 +170,9 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
{
|
||||
RouteSearch.Metrics metrics2;
|
||||
bool flag = Graph.Shared.FindRoute(start, end, autoEngineer, null, out metrics2);
|
||||
Multiplayer.SendError(StateManager.Shared.PlayersManager.LocalPlayer, flag ? (selectedLoco.DisplayName + " Train too long to navigate to waypoint.") : (selectedLoco.DisplayName + " Unable to find a path to waypoint."), AlertLevel.Error);
|
||||
} else
|
||||
Multiplayer.SendError(StateManager.Shared.PlayersManager.LocalPlayer, flag ? (getDictKey(selectedLoco) + " Train too long to navigate to waypoint.") : (getDictKey(selectedLoco) + " Unable to find a path to waypoint."), AlertLevel.Error);
|
||||
}
|
||||
else
|
||||
{
|
||||
var mw = (location: end, carId: string.Empty);
|
||||
aeoh.SetWaypoint(mw.location, mw.carId);
|
||||
@@ -152,10 +181,157 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return __result;
|
||||
}
|
||||
|
||||
private static Location RouteStartLocation(AutoEngineerWaypointControls __instance, BaseLocomotive _locomotive) {
|
||||
private static List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> BuildJumpToOptions(AutoEngineerWaypointControls __instance, BaseLocomotive selectedLoco)
|
||||
{
|
||||
List<(string destinationId, string destination, float? distance, float sortDistance, Location? location)> jumpTos = new();
|
||||
foreach (OpsCarPosition ocp in locoConsistDestinations)
|
||||
{
|
||||
string destName = ocp.DisplayName;
|
||||
string destId = ocp.Identifier;
|
||||
float? distance = null;
|
||||
float sortdistance = 0f;
|
||||
if (
|
||||
Graph.Shared.TryGetLocationFromPoint(
|
||||
ocp.Spans?.FirstOrDefault().GetSegments().FirstOrDefault(),
|
||||
ocp.Spans?.FirstOrDefault()?.GetCenterPoint() ?? default,
|
||||
200f,
|
||||
out Location destLoc
|
||||
)
|
||||
)
|
||||
{
|
||||
float trainMomentum = 0f;
|
||||
Location start = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.RouteStartLocation(out trainMomentum) : RouteStartLocation(__instance, selectedLoco);
|
||||
HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer;
|
||||
List<RouteSearch.Step> list = new List<RouteSearch.Step>();
|
||||
var totLen = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.CalculateTotalLength() : CalculateTotalLength(selectedLoco);
|
||||
distance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum)
|
||||
? metrics.Distance
|
||||
: null;
|
||||
sortdistance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out metrics, checkForCars: false, 0f, trainMomentum)
|
||||
? metrics.Distance
|
||||
: float.MaxValue;
|
||||
}
|
||||
;
|
||||
_log.Debug($"{getDictKey(selectedLoco)} -> {destName} {destId} {distance?.ToString()}");
|
||||
jumpTos.Add((
|
||||
destinationId: destId,
|
||||
destination: $"WP> {destName}"
|
||||
, distance: distance
|
||||
, sortdistance: sortdistance
|
||||
, location: (Location?)destLoc
|
||||
));
|
||||
}
|
||||
|
||||
if (selectedLoco.TryGetTimetableTrain(out Timetable.Train t))
|
||||
{
|
||||
//_log.Information($"{getDictKey(selectedLoco)} -> {t.DisplayStringLong}");
|
||||
foreach (var e in t.Entries)
|
||||
{
|
||||
var stp = TimetableController.Shared.GetAllStations().FirstOrDefault(ps => ps.code == e.Station);
|
||||
//_log.Information($"{getDictKey(selectedLoco)} -> {t.DisplayStringLong} -> {e.Station} {stp}");
|
||||
if (stp != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
string destName = t.TrainType == Timetable.TrainType.Passenger ? stp.passengerStop.DisplayName : stp.DisplayName;
|
||||
string destId = t.TrainType == Timetable.TrainType.Passenger ? stp.passengerStop.identifier : stp.code;
|
||||
float? distance = null;
|
||||
float sortdistance = 0f;
|
||||
if (
|
||||
Graph.Shared.TryGetLocationFromPoint(
|
||||
stp.passengerStop.TrackSpans?.FirstOrDefault().GetSegments().FirstOrDefault(),
|
||||
stp.passengerStop.TrackSpans?.FirstOrDefault()?.GetCenterPoint() ?? default,
|
||||
200f,
|
||||
out Location destLoc
|
||||
)
|
||||
)
|
||||
{
|
||||
float trainMomentum = 0f;
|
||||
Location start = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.RouteStartLocation(out trainMomentum) : RouteStartLocation(__instance, selectedLoco);
|
||||
HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer;
|
||||
List<RouteSearch.Step> list = new List<RouteSearch.Step>();
|
||||
var totLen = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.CalculateTotalLength() : CalculateTotalLength(selectedLoco);
|
||||
distance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum)
|
||||
? metrics.Distance
|
||||
: null;
|
||||
sortdistance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out metrics, checkForCars: false, 0f, trainMomentum)
|
||||
? metrics.Distance
|
||||
: float.MaxValue;
|
||||
}
|
||||
;
|
||||
_log.Debug($"{getDictKey(selectedLoco)} -> {destName} {destId} {distance?.ToString()}");
|
||||
jumpTos.Add((
|
||||
destinationId: destId,
|
||||
destination: $"{t.DisplayStringLong} > {destName}"
|
||||
, distance: distance
|
||||
, sortdistance: sortdistance
|
||||
, location: (Location?)destLoc
|
||||
));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warning(ex, $"Timetable entry not added to AE gear cog options {stp}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jumpTos;
|
||||
}
|
||||
|
||||
private static void IterateCarsDetectDestinations(
|
||||
AutoEngineerWaypointControls __instance,
|
||||
OptionsDropdownConfiguration __result,
|
||||
BaseLocomotive selectedLoco,
|
||||
int numberOfCars,
|
||||
HashSet<OpsCarPosition?> destinations,
|
||||
out List<DropdownMenu.RowData> rowDatas,
|
||||
out Action<int> func,
|
||||
out int origCount,
|
||||
out int maxRowOrig,
|
||||
out AutoEngineerOrdersHelper aeoh
|
||||
)
|
||||
{
|
||||
rowDatas = __result.Rows.ToList();
|
||||
func = __result.OnRowSelected;
|
||||
origCount = rowDatas.Count;
|
||||
maxRowOrig = origCount - 1;
|
||||
aeoh = new AutoEngineerOrdersHelper(persistence: new AutoEngineerPersistence(selectedLoco.KeyValueObject), locomotive: selectedLoco);
|
||||
string locoKey = getDictKey(selectedLoco);
|
||||
|
||||
var dropped =
|
||||
locoConsistDestinations.Except(destinations).ToHashSet(); //are no longer here
|
||||
|
||||
_log.Debug($"{locoKey} --> [{destinations.Count}] -> Seen -> {string.Join(Environment.NewLine, destinations.Select(k => k.Value.DisplayName))}");
|
||||
_log.Debug($"{locoKey} --> [{locoConsistDestinations.Count}] -> Cache -> {string.Join(Environment.NewLine, locoConsistDestinations.Select(k => $"{locoKey}:{k.Value.DisplayName}"))}");
|
||||
_log.Debug($"{locoKey} --> [{dropped.Count}] -> removed -> {string.Join(Environment.NewLine, dropped.Select(k => k.Value.DisplayName))}");
|
||||
locoConsistDestinations = destinations.ToList().ToHashSet(); //remove ones that are no longer here
|
||||
}
|
||||
|
||||
private static void PrepLocoUsage(AutoEngineerWaypointControls __instance, out BaseLocomotive selectedLoco, out int numberOfCars)
|
||||
{
|
||||
//wire up that loco
|
||||
selectedLoco = __instance.Locomotive;
|
||||
numberOfCars = selectedLoco?.set.NumberOfCars ?? -1;
|
||||
_log.Debug($"{selectedLoco?.id} --> HI BOB[{numberOfCars}]");
|
||||
}
|
||||
|
||||
private static OpsCarPosition? GetCarDestinationIdentifier(Car c)
|
||||
{
|
||||
OpsCarPosition? destination = null;
|
||||
if (c.TryGetOverrideDestination(OverrideDestination.Repair, OpsController.Shared, out (OpsCarPosition, string)? result))
|
||||
destination = result.Value.Item1;
|
||||
|
||||
if (!destination.HasValue && c.Waybill.HasValue && !c.Waybill.Value.Completed)
|
||||
destination = c.Waybill.Value.Destination;
|
||||
return destination;
|
||||
}
|
||||
|
||||
private static Location RouteStartLocation(AutoEngineerWaypointControls __instance, BaseLocomotive _locomotive)
|
||||
{
|
||||
bool num = _locomotive.IsStopped();
|
||||
bool? flag = (num ? null : new bool?(_locomotive.velocity >= 0f));
|
||||
|
||||
@@ -178,47 +354,16 @@ internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch
|
||||
}
|
||||
|
||||
return num + 1.04f * (float)(coupledCarsCached.Count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddObserversToCar(AutoEngineerWaypointControls __instance, Car c)
|
||||
[HarmonyPatch(typeof(LocomotiveControlsUIAdapter))]
|
||||
[HarmonyPatch(nameof(LocomotiveControlsUIAdapter.UpdateOptionsDropdown))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class LocomotiveControlsUIAdapter_UpdateOptionsDropdown_Prefix
|
||||
{
|
||||
static bool Prefix(LocomotiveControlsUIAdapter __instance)
|
||||
{
|
||||
AddObserver(__instance, c, Car.KeyOpsWaybill);
|
||||
AddObserver(__instance, c, Car.KeyOpsRepairDestination);
|
||||
foreach (Car.LogicalEnd logicalEnd in CarInspector_PopulateCarPanel_Patch.ends)
|
||||
{
|
||||
AddObserver(__instance, c, Car.KeyValueKeyFor(Car.EndGearStateKey.IsCoupled, c.LogicalToEnd(logicalEnd)), true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddObserver(AutoEngineerWaypointControls __instance, Model.Car car, string key, bool clearCarCache = false)
|
||||
{
|
||||
_keyChangeObservers.Add(
|
||||
car.KeyValueObject.Observe(
|
||||
key,
|
||||
delegate (Value value)
|
||||
{
|
||||
_log.Information($"{car.DisplayName} OBSV {key}: {value}; recalcing {recalcing}");
|
||||
if (recalcing) return;
|
||||
try
|
||||
{
|
||||
foreach(var o in _keyChangeObservers)
|
||||
{
|
||||
o.Dispose();
|
||||
}
|
||||
var loco = TrainController.Shared.SelectedLocomotive;
|
||||
TrainController.Shared.SelectedCar = null;
|
||||
_keyChangeObservers.Clear();
|
||||
recalcing = true;
|
||||
new WaitForSeconds(0.25f);
|
||||
TrainController.Shared.SelectedCar = loco;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ internal class CarInspector_PopulateCarPanel_Patch
|
||||
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);
|
||||
var newSubTitle = () => string.Format("{0}, {1:N0}T, {2:N0}ft, {3:N0} mph", consistLength.Pluralize("car"), tonnage, lengthInMeters, __instance._car.VelocityMphAbs);
|
||||
|
||||
field.AddLabel(() => newSubTitle(), UIPanelBuilder.Frequency.Fast)
|
||||
.Tooltip("Consist Info", "Reflects info about consist.").FlexibleWidth();
|
||||
@@ -227,8 +227,8 @@ internal class CarInspector_PopulateCarPanel_Patch
|
||||
float originalTimeCost = consist.CalculateCostForAutoEngineerEndGearSetting();
|
||||
float timeCost = originalTimeCost;
|
||||
float crewCost = timeCost / 3600; //hours of time deducted from caboose.
|
||||
var tsString = crewCost.FormatCrewHours(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad().description);
|
||||
Car? cabooseWithAvailCrew = car.FindMyCaboose(crewCost, buttonsHaveCost);
|
||||
var tsString = crewCost.FormatCrewHours(OpsController_AnnounceCoalescedPayments_Patch.CrewLoadHours.description);
|
||||
Car? cabooseWithAvailCrew = car.FindMyCabooseWithLoadRequirement(crewCost, buttonsHaveCost);
|
||||
if (cabooseWithAvailCrew == null) timeCost *= 1.5f;
|
||||
var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose";
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ internal class CarPickable_Activate_Patch
|
||||
var consist = car.EnumerateCoupled();
|
||||
bool handbrakesApplied = consist.Any(c => c.HandbrakeApplied());
|
||||
bool airSystemIssues = consist.Any(c => c.EndAirSystemIssue());
|
||||
Func<bool> cabooseNear = () => (bool)car.FindMyCaboose(0.0f, false);
|
||||
Func<bool> cabooseNear = () => (bool)car.FindMyCabooseSansLoadRequirement();
|
||||
bool needsOiling = GameInput.IsShiftDown && consist.All(c => c.IsStopped()) && consist.Any(c => c.NeedsOiling || c.HasHotbox) && (!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || cabooseNear());
|
||||
var chargeIt = handbrakesApplied || airSystemIssues || needsOiling;
|
||||
//CTRL + ALT + SHIFT : BrakesAngleCocksAndOiling
|
||||
|
||||
@@ -4,6 +4,7 @@ using Railloader;
|
||||
using RMROC451.TweaksAndThings.Enums;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RollingStock;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UI;
|
||||
using UI.ContextMenu;
|
||||
@@ -35,8 +36,6 @@ internal class CarPickable_HandleShowContextMenu_Patch
|
||||
});
|
||||
if (GameInput.IsShiftDown)
|
||||
{
|
||||
|
||||
|
||||
if (!car.EnumerateCoupled().Any(c => !c.SupportsBleed()))
|
||||
{
|
||||
shared.AddButton(ContextMenuQuadrant.Brakes, $"Bleed Consist", SpriteName.Bleed, delegate
|
||||
@@ -52,7 +51,7 @@ internal class CarPickable_HandleShowContextMenu_Patch
|
||||
|
||||
if (car.EnumerateCoupled().Any(c => c.EndAirSystemIssue()))
|
||||
{
|
||||
shared.AddButton(ContextMenuQuadrant.Unused2, $"Air Up Consist", SpriteName.Select, delegate
|
||||
shared.AddButton(ContextMenuQuadrant.General, $"Air Up Consist", SpriteName.Select, delegate
|
||||
{
|
||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
|
||||
});
|
||||
@@ -76,7 +75,9 @@ internal class CarPickable_HandleShowContextMenu_Patch
|
||||
CameraSelector.shared.FollowCar(car);
|
||||
});
|
||||
|
||||
shared.Show(car.DisplayName);
|
||||
string secondaryLine = car.Waybill.HasValue ? $"{Environment.NewLine}{car.Waybill.Value.Destination.DisplayName}" : string.Empty;
|
||||
secondaryLine = secondaryLine.Length > 10 + Environment.NewLine.Length ? $"{secondaryLine.Substring(0, 7+ Environment.NewLine.Length)}..." : secondaryLine;
|
||||
shared.Show($"{car.DisplayName}{secondaryLine}");
|
||||
shared.BuildItemAngles();
|
||||
shared.StartCoroutine(shared.AnimateButtonsShown());
|
||||
return false;
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using Helpers;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UI;
|
||||
using UI.ContextMenu;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(UI.ContextMenu.ContextMenu))]
|
||||
[HarmonyPatch(nameof(UI.ContextMenu.ContextMenu.Show), typeof(string))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class ContextMenu_Show_Patch
|
||||
{
|
||||
static bool Prefix(UI.ContextMenu.ContextMenu __instance, string centerText)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled()) return true;
|
||||
|
||||
if (!__instance.GetRootCanvas(out var rootCanvas))
|
||||
{
|
||||
Log.Warning("Couldn't get root canvas");
|
||||
return true;
|
||||
}
|
||||
__instance.CancelHideCoroutine();
|
||||
if (__instance.contentRectTransform.childCount >= 1) __instance.contentRectTransform.GetChild(0).DestroyAllChildren(); //YOINK DEM CIRCLES!
|
||||
__instance.SetupTemplate(rootCanvas);
|
||||
__instance.centerLabel.text = centerText;
|
||||
Canvas componentInParent = ((Component)__instance.contentRectTransform).GetComponentInParent<Canvas>();
|
||||
Vector3 mousePosition = Input.mousePosition;
|
||||
Vector2 val = componentInParent.ScreenToCanvasPosition(mousePosition).XY();
|
||||
Vector2 renderingDisplaySize = rootCanvas.renderingDisplaySize;
|
||||
float num = __instance.radius + 50f;
|
||||
if (val.x < num)
|
||||
{
|
||||
val.x = num;
|
||||
}
|
||||
if (val.x > renderingDisplaySize.x - num)
|
||||
{
|
||||
val.x = renderingDisplaySize.x - num;
|
||||
}
|
||||
if (val.y < num)
|
||||
{
|
||||
val.y = num;
|
||||
}
|
||||
if (val.y > renderingDisplaySize.y - num)
|
||||
{
|
||||
val.y = renderingDisplaySize.y - num;
|
||||
}
|
||||
__instance.contentRectTransform.anchoredPosition = val;
|
||||
__instance.BuildItemAngles();
|
||||
|
||||
((MonoBehaviour)__instance).StartCoroutine(__instance.AnimateButtonsShown());
|
||||
((Component)__instance.contentRectTransform).gameObject.SetActive(true);
|
||||
UI.ContextMenu.ContextMenu.IsShown = true;
|
||||
__instance._blocker = __instance.CreateBlocker(rootCanvas);
|
||||
GameInput.RegisterEscapeHandler(GameInput.EscapeHandler.Transient, delegate
|
||||
{
|
||||
__instance.Hide();
|
||||
return true;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPatch(typeof(UI.ContextMenu.ContextMenu))]
|
||||
[HarmonyPatch(nameof(UI.ContextMenu.ContextMenu.DefaultAngleForItem), typeof(ContextMenuQuadrant), typeof(int), typeof(int))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class ContextMenu_DefaultAngleForItem_Patch
|
||||
{
|
||||
static bool Prefix(UI.ContextMenu.ContextMenu __instance, ref float __result, ContextMenuQuadrant quadrant, int index, int quadrantItemCount)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled()) return true;
|
||||
|
||||
|
||||
int num = quadrant switch
|
||||
{
|
||||
ContextMenuQuadrant.General => 0,
|
||||
ContextMenuQuadrant.Unused1 => 90,
|
||||
ContextMenuQuadrant.Brakes => 180,
|
||||
ContextMenuQuadrant.Unused2 => -90,
|
||||
_ => throw new ArgumentOutOfRangeException("quadrant", quadrant, null),
|
||||
};
|
||||
if (quadrantItemCount <= 1)
|
||||
{
|
||||
__result = num;
|
||||
return false;
|
||||
}
|
||||
int num2 = ((quadrantItemCount <= 3) ? 30 : (90 / (quadrantItemCount - 1)));
|
||||
__result = (float)num + -0.5f * (float)((quadrantItemCount - 1) * num2) + (float)(num2 * index);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HarmonyPatch(typeof(UI.ContextMenu.ContextMenu))]
|
||||
[HarmonyPatch(nameof(UI.ContextMenu.ContextMenu.AddButton), typeof(ContextMenuQuadrant), typeof(string), typeof(Sprite), typeof(Action))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class ContextMenu_AddButton_Patch
|
||||
{
|
||||
static bool Prefix(UI.ContextMenu.ContextMenu __instance, ContextMenuQuadrant quadrant, string title, Sprite sprite, Action action)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled()) return true;
|
||||
|
||||
|
||||
List<ContextMenuItem> list = __instance._quadrants[(int)quadrant];
|
||||
int index = list.Count;
|
||||
ContextMenuItem contextMenuItem = UnityEngine.Object.Instantiate<ContextMenuItem>(__instance.itemPrefab, (Transform)(object)__instance.contentRectTransform);
|
||||
contextMenuItem.image.sprite = sprite;
|
||||
contextMenuItem.label.text = title;
|
||||
contextMenuItem.OnClick = delegate
|
||||
{
|
||||
action();
|
||||
__instance.Hide((quadrant, index));
|
||||
};
|
||||
((Component)contextMenuItem).gameObject.AddComponent<LayoutElement>().preferredHeight = 30f;
|
||||
list.Add(contextMenuItem);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ internal class EngineRosterPanel_Populate_Patch
|
||||
__instance._window.Title = __instance._window.Title.Split(':')[0].Trim();
|
||||
if (!tweaksAndThings.IsEnabled()) return true;
|
||||
|
||||
var hiddenEntries = rows.Where(r => r.Engine.IsMuEnabled && !r.IsSelected && !r.IsFavorite).Select(r => r.Engine.id) ?? Enumerable.Empty<string>();
|
||||
var hiddenEntries = rows.Where(r => r.Engine.locomotiveControl.air.IsCutOut && !r.IsSelected && !r.IsFavorite).Select(r => r.Engine.id) ?? Enumerable.Empty<string>();
|
||||
|
||||
if (hiddenEntries.Any()) __instance._window.Title =string.Format("{0} : {1}", __instance._window.Title, $"Hidden MU Count [{hiddenEntries.Count()}]");
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ internal class EngineRosterRow_Refresh_Patch
|
||||
bool cabooseRequirementFulfilled =
|
||||
!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter()
|
||||
|| consist.ConsistNoFreight()
|
||||
|| (bool)engineOrTender.FindMyCaboose(0.0f, false);
|
||||
|| (bool)engineOrTender.FindMyCabooseSansLoadRequirement();
|
||||
float offendingPercentage = 100f;
|
||||
|
||||
foreach (Car loco in locos)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using Game;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using Helpers;
|
||||
using Model;
|
||||
using Model.Ops.Timetable;
|
||||
using Newtonsoft.Json;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
@@ -21,6 +22,7 @@ namespace RMROC451.TweaksAndThings.Patches;
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class ExpandedConsole_Add_Patch
|
||||
{
|
||||
private static Serilog.ILogger _log => Log.ForContext<ExpandedConsole_Add_Patch>();
|
||||
private static void Prefix(ref UI.Console.Console.Entry entry)
|
||||
{
|
||||
entry.Text = $"{entry.Timestamp} : {entry.Text}";
|
||||
@@ -28,12 +30,26 @@ internal class ExpandedConsole_Add_Patch
|
||||
SendMs((UI.Console.Console.Entry?)entry);
|
||||
}
|
||||
|
||||
|
||||
private static string hold => @"```ansi
|
||||
[2;40m[0m[2;40m[2;34m■[0m[2;40m[0m[2;34m[2;40m[0m[2;34m[0m[2;40m[1;2m[1;34m{loco}[0m[1;40m[0m[2;40m[0m[2;40m[1;2m[1;34m[0m[1;40m[0m[2;40m[0m[2;40m[2;34m■[0m[2;40m[0m[2;34m[2;40m[0m[2;34m[0m {msg}
|
||||
```";
|
||||
|
||||
private static string west => @"```ansi
|
||||
[2;40m[0m[2;31m[2;40m◀{loco}[0m[2;31m[0m[2;40m[2;31m [0m[2;40m[0m[2;40m[0m {msg}
|
||||
```";
|
||||
|
||||
private static string east => @"```ansi
|
||||
[2;40m[0m[2;32m[0m[2;40m [0m[2;36m[0m[2;40m[0m[2;36m[2;40m{loco}▶[0m[2;36m[0m {msg}
|
||||
```";
|
||||
|
||||
internal static void SendMs(UI.Console.Console.Entry? entry, string? text = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (entry is null && !String.IsNullOrEmpty(text)) entry = new() { Text = text };
|
||||
var msgText = entry?.Text ?? string.Empty;
|
||||
if (msgText.StartsWith("Usage:")) return;
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
StateManager shared = StateManager.Shared;
|
||||
GameStorage gameStorage = shared.Storage;
|
||||
@@ -50,31 +66,45 @@ internal class ExpandedConsole_Add_Patch
|
||||
|
||||
var carId = t.IsMatch(msgText) ? Regex.Match(msgText, "car:(.*?)\"").Groups[1].Captures[0].ToString() : string.Empty;
|
||||
Model.Car? car = TrainController.Shared.CarForString(carId);
|
||||
var data = UpdateCarText(car);
|
||||
bool engineInMessage = car?.IsLocomotive ?? false;
|
||||
var image = engineInMessage ?
|
||||
new
|
||||
{
|
||||
url = string.Empty
|
||||
} :
|
||||
null;
|
||||
string msgToSend = string.Empty;
|
||||
string desc = Regex.Replace(msgText, "<.*?>", "");
|
||||
desc = !!car ? desc.Replace(car?.DisplayName ?? string.Empty, string.Empty) : desc;
|
||||
desc = desc.Trim();//.Replace(": ", "\n");
|
||||
|
||||
if (engineInMessage)
|
||||
if (!!car && engineInMessage)
|
||||
{
|
||||
CTCPanelMarkerManager cTCPanelMarkerManager = UnityEngine.Object.FindObjectOfType<CTCPanelMarkerManager>();
|
||||
|
||||
CTCPanelMarker marker = cTCPanelMarkerManager?._markers?.Values?.FirstOrDefault(v => v.TooltipInfo.Text.Contains(car.Ident.RoadNumber));
|
||||
|
||||
string color = CTCPanelMarker.InferColorFromText(car?.DisplayName).HexString().Replace("#", string.Empty);
|
||||
if (marker != null)
|
||||
|
||||
string markerText = marker?.TooltipInfo.Text ?? string.Empty;
|
||||
if (markerText.StartsWith(">") || markerText.EndsWith(">") || data.Item2 == Timetable.Direction.East || msgText.Contains("*-"))
|
||||
{
|
||||
color = CTCPanelMarker.InferColorFromText(marker.TooltipInfo.Text).HexString().Replace("#", string.Empty);
|
||||
msgToSend = east.Replace("{loco}", car?.DisplayName.Replace(" ", string.Empty)).Replace("{msg}", desc);
|
||||
}
|
||||
image = new
|
||||
else if (markerText.StartsWith("<") || markerText.EndsWith("<") || data.Item2 == Timetable.Direction.West || msgText.Contains("-*"))
|
||||
{
|
||||
url = $"https://img.shields.io/badge/{car.DisplayName.Replace(" ", "%20")}-%20-{color}.png"
|
||||
};
|
||||
|
||||
msgToSend = west.Replace("{loco}", car?.DisplayName.Replace(" ", string.Empty)).Replace("{msg}", desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgToSend = hold.Replace("{loco}", car?.DisplayName.Replace(" ", string.Empty)).Replace("{msg}", desc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msgToSend = desc;
|
||||
}
|
||||
msgToSend = msgToSend
|
||||
.Replace(" , ", ", ")
|
||||
.Replace(", :", " :")
|
||||
.Replace(" ; ", "; ")
|
||||
.Replace("*-", string.Empty)
|
||||
.Replace("#", string.Empty)
|
||||
.Replace("-*", string.Empty)
|
||||
.Trim();
|
||||
|
||||
var SuccessWebHook = new
|
||||
{
|
||||
@@ -83,9 +113,7 @@ internal class ExpandedConsole_Add_Patch
|
||||
{
|
||||
new
|
||||
{
|
||||
description= Regex.Replace(msgText, "<.*?>", "").Replace(": ", "\n"),
|
||||
timestamp=DateTime.UtcNow,
|
||||
image
|
||||
description= msgToSend
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -103,6 +131,26 @@ internal class ExpandedConsole_Add_Patch
|
||||
}
|
||||
}
|
||||
|
||||
public static (string , Timetable.Direction?) UpdateCarText(Car car)
|
||||
{
|
||||
string output = string.Empty;
|
||||
Timetable.Direction? dir = null;
|
||||
if (car?.IsLocomotive ?? false)
|
||||
{
|
||||
if (StateManager.Shared.PlayersManager.TrainCrewForId(car.trainCrewId, out var trainCrew))
|
||||
{
|
||||
output = trainCrew.Name;
|
||||
if (TimetableController.Shared.TryGetTrainForTrainCrew(trainCrew, out Timetable.Train timetableTrain))
|
||||
{
|
||||
dir = timetableTrain.Direction;
|
||||
output += " (Train " + timetableTrain.DisplayStringShort + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
return (output, dir);
|
||||
}
|
||||
|
||||
|
||||
public static GameDateTime RealNow()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
||||
@@ -48,8 +48,7 @@ internal class InterchangedIndustryLoader_ServiceInterchange_Patch
|
||||
if (num4 > 0)
|
||||
{
|
||||
var canAfford = shared.CanAfford(num4);
|
||||
penalty = Mathf.CeilToInt(!canAfford ? num4 * 0.2f : 0);
|
||||
__instance.Industry.ApplyToBalance(-num4, __instance.ledgerCategory, null, 0, quiet: true);
|
||||
penalty += Mathf.CeilToInt(!canAfford ? num4 * 0.2f : 0);
|
||||
|
||||
num2 += num4;
|
||||
num3++;
|
||||
|
||||
@@ -24,7 +24,7 @@ internal class CarExtensions_LoadString_Patch
|
||||
{
|
||||
public static bool Prefix(CarLoadInfo info, Load load, ref string __result)
|
||||
{
|
||||
bool output = load.id == OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad().id;
|
||||
bool output = load.id == OpsController_AnnounceCoalescedPayments_Patch.CrewLoadHours.id;
|
||||
if (output) __result = info.Quantity.FormatCrewHours(load.description);
|
||||
|
||||
return !output;
|
||||
@@ -38,7 +38,7 @@ internal class CarPrototypeLibrary_LoadForId_Patch
|
||||
{
|
||||
public static bool Prefix(string loadId, ref Load __result)
|
||||
{
|
||||
Load load = OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad();
|
||||
Load load = OpsController_AnnounceCoalescedPayments_Patch.CrewLoadHours;
|
||||
if (loadId == load.id) __result = load;
|
||||
|
||||
return __result == null;
|
||||
@@ -70,7 +70,9 @@ internal class OpsController_AnnounceCoalescedPayments_Patch
|
||||
typeof(RepairTrack)
|
||||
};
|
||||
|
||||
public static Load CrewHoursLoad()
|
||||
private static Load _crewLoadHours;
|
||||
internal static Load CrewLoadHours => _crewLoadHours ?? CrewHoursLoad();
|
||||
private static Load CrewHoursLoad()
|
||||
{
|
||||
Load load = (Load)ScriptableObject.CreateInstance(typeof(Load));
|
||||
load.name = "crew-hours";
|
||||
@@ -96,6 +98,7 @@ internal class OpsController_AnnounceCoalescedPayments_Patch
|
||||
var data = car.QuantityCapacityOfLoad(CrewHoursLoad());
|
||||
if ((data.quantity + quantityToLoad > data.capacity) && data.quantity < data.capacity)
|
||||
{
|
||||
quantityToLoad = data.capacity; //ensure topping off
|
||||
Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Caboose crew topped off.\"");
|
||||
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, false);
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ internal class StateManager_OnDayDidChange_Patch
|
||||
{
|
||||
foreach (var car in TrainController.Shared.Cars.Where(Car_Extensions.IsCaboose))
|
||||
{
|
||||
var data = car.QuantityCapacityOfLoad(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad());
|
||||
var data = car.QuantityCapacityOfLoad(OpsController_AnnounceCoalescedPayments_Patch.CrewLoadHours);
|
||||
if (data.quantity < data.capacity)
|
||||
{
|
||||
Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Caboose crew topped off.\"");
|
||||
new OpsCarAdapter(car, OpsController.Shared).Load(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad(), data.capacity - data.quantity);
|
||||
new OpsCarAdapter(car, OpsController.Shared).Load(OpsController_AnnounceCoalescedPayments_Patch.CrewLoadHours, data.capacity - data.quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using KeyValue.Runtime;
|
||||
using Model;
|
||||
using Model.Ops;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RollingStock;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UI.Tags;
|
||||
using static Unity.IO.LowLevel.Unsafe.AsyncReadManagerMetrics;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
@@ -43,7 +47,7 @@ internal class TagController_UpdateTag_Patch
|
||||
//if (car.EnableOiling) tags.Add(car.HasHotbox ? TextSprites.Hotbox : $"<cspace=-1em>{TextSprites.Warning}{car.Oiled.TriColorPiePercent(1)}</cspace>");
|
||||
if (car.EnableOiling) tags.Add(car.HasHotbox ? TextSprites.Hotbox : car.Oiled.TriColorPiePercent(1, oilSpriteName));
|
||||
IEnumerable<Car> consist = car.EnumerateCoupled().Where(c => c.EnableOiling);
|
||||
Func<bool> cabooseRequirementFulfilled = () => (!cabooseRequired || consist.ConsistNoFreight() || car.FindMyCaboose(0.0f, false));
|
||||
Func<bool> cabooseRequirementFulfilled = () => (!cabooseRequired || consist.ConsistNoFreight() || (bool)car.FindMyCabooseSansLoadRequirement());
|
||||
if (StateManager.Shared.Storage.OilFeature
|
||||
&& car.IsLocomotive
|
||||
&& !car.NeedsOiling
|
||||
@@ -54,6 +58,18 @@ internal class TagController_UpdateTag_Patch
|
||||
if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
|
||||
if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
|
||||
|
||||
if (car.IsPassengerCar())
|
||||
{
|
||||
PassengerMarker? passengerMarker = car.GetPassengerMarker();
|
||||
if (passengerMarker.HasValue)
|
||||
{
|
||||
IEnumerable<string> loadInfo = car.PassengerCountString(passengerMarker).Split('/');
|
||||
//string item4 = CarPickable.PassengerString(car, passengerMarker.Value);
|
||||
string val = TextSprites.PiePercent(float.Parse(loadInfo.First()), float.Parse(loadInfo.Last())) + $" {car.PassengerCountString(passengerMarker)} Passengers";
|
||||
tagCallout.callout.Text = tagCallout.callout.Text.Contains("Empty") ? tagCallout.callout.Text.Replace("Empty", val) : tagCallout.callout.Text + $"\n{val}";
|
||||
}
|
||||
}
|
||||
|
||||
tagCallout.callout.Title =
|
||||
tags.Any() switch
|
||||
{
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using Railloader;
|
||||
using UI.ContextMenu;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(WedgeImage))]
|
||||
[HarmonyPatch(nameof(WedgeImage.OnPopulateMesh), typeof(VertexHelper))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class WedgeImage_OnPopulateMesh_Patch
|
||||
{
|
||||
private static void Postfix(VertexHelper vh)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled()) return;
|
||||
|
||||
vh.Clear(); //clear the image backgrounds for now.
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public class Settings
|
||||
bool cabooseRequiredForLocoTagOilIndication,
|
||||
bool servicingFundPenalty,
|
||||
bool safetyFirst,
|
||||
bool safetyFirstClientEnforce,
|
||||
CrewHourLoadMethod loadCrewHoursMethod,
|
||||
float cabeeseSearchRadiusFtInMeters,
|
||||
bool trainBrakeDisplayShowsColorsInCalloutMode
|
||||
@@ -42,6 +43,7 @@ public class Settings
|
||||
CabooseRequiredForLocoTagOilIndication = cabooseRequiredForLocoTagOilIndication;
|
||||
ServicingFundPenalty = servicingFundPenalty;
|
||||
SafetyFirst = safetyFirst;
|
||||
SafetyFirstClientEnforce = safetyFirstClientEnforce;
|
||||
LoadCrewHoursMethod = loadCrewHoursMethod;
|
||||
CabeeseSearchRadiusFtInMeters = cabeeseSearchRadiusFtInMeters;
|
||||
TrainBrakeDisplayShowsColorsInCalloutMode = trainBrakeDisplayShowsColorsInCalloutMode;
|
||||
@@ -57,6 +59,7 @@ public class Settings
|
||||
public bool CabooseRequiredForLocoTagOilIndication;
|
||||
public bool ServicingFundPenalty;
|
||||
public bool SafetyFirst;
|
||||
public bool SafetyFirstClientEnforce;
|
||||
public CrewHourLoadMethod LoadCrewHoursMethod;
|
||||
public float CabeeseSearchRadiusFtInMeters;
|
||||
public bool TrainBrakeDisplayShowsColorsInCalloutMode;
|
||||
@@ -129,13 +132,15 @@ public static class SettingsExtensions
|
||||
public static bool RequireConsistCabooseForOilerAndHotboxSpotter(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false;
|
||||
public static bool CabooseNonMotiveAllowedSetting(this TweaksAndThingsPlugin input, Car car) =>
|
||||
input.EndGearHelpersRequirePayment() && !car.MotivePower() && (bool)car.FindMyCaboose(0.0f, false);
|
||||
input.EndGearHelpersRequirePayment() && !car.MotivePower() && (bool)car.FindMyCabooseSansLoadRequirement();
|
||||
public static bool CabooseRequiredForLocoOilIndicator(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.CabooseRequiredForLocoTagOilIndication ?? false;
|
||||
public static bool ServiceFundPenalties(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.ServicingFundPenalty ?? false;
|
||||
public static bool SafetyFirst(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.SafetyFirst ?? false;
|
||||
public static bool SafetyFirstClientEnforce(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.SafetyFirstClientEnforce ?? true;
|
||||
public static bool DayLoadCrewHours(this TweaksAndThingsPlugin input) =>
|
||||
(input?.settings?.LoadCrewHoursMethod ?? CrewHourLoadMethod.Tracks) == CrewHourLoadMethod.Daily;
|
||||
public static bool TrainBrakeDisplayShowsColorsInCalloutMode(this TweaksAndThingsPlugin input) =>
|
||||
|
||||
@@ -40,7 +40,6 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
||||
|
||||
static TweaksAndThingsPlugin()
|
||||
{
|
||||
Log.Debug("Hello! Static Constructor was called!");
|
||||
}
|
||||
|
||||
public TweaksAndThingsPlugin(IModdingContext moddingContext, IModDefinition self)
|
||||
@@ -58,7 +57,6 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
logger.Debug("OnEnable() was called!");
|
||||
var harmony = new Harmony(modDefinition.Id);
|
||||
harmony.PatchCategory(modDefinition.Id.Replace(".", string.Empty));
|
||||
}
|
||||
@@ -72,13 +70,10 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
||||
|
||||
public void Update()
|
||||
{
|
||||
logger.Verbose("UPDATE()");
|
||||
}
|
||||
|
||||
public void ModTabDidOpen(UIPanelBuilder builder)
|
||||
{
|
||||
logger.Debug("Daytime!");
|
||||
|
||||
if (settings == null) settings = new();
|
||||
if (!settings?.WebhookSettingsList?.Any() ?? true) settings.WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
|
||||
if (settings?.EngineRosterFuelColumnSettings == null) settings.EngineRosterFuelColumnSettings = new();
|
||||
@@ -208,6 +203,23 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
||||
).Tooltip("Safety First", $@"On non-express timetabled consists, a caboose is required in the consist increase AE max speed > 20 in {Enum.GetName(typeof(AutoEngineerMode), AutoEngineerMode.Road)}/{Enum.GetName(typeof(AutoEngineerMode), AutoEngineerMode.Waypoint)} mode.");
|
||||
#endregion
|
||||
|
||||
#region SafetyFirstClient
|
||||
if (settings?.SafetyFirst ?? false)
|
||||
{
|
||||
builder.Spacer(spacing);
|
||||
builder.AddFieldToggle(
|
||||
"Safety First! (Enforce Client Speed Restrictions)",
|
||||
() => settings?.SafetyFirstClientEnforce ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.SafetyFirstClientEnforce = enabled;
|
||||
builder.Rebuild();
|
||||
}
|
||||
).Tooltip("Safety First! (Enforce Client Speed Restrictions)", $@"Enforce cabeese dominance on clients; uncheck to allow clients to override the 20mph restriction.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
private void UiUpdates(UIPanelBuilder builder)
|
||||
@@ -347,7 +359,6 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
||||
|
||||
public void ModTabDidClose()
|
||||
{
|
||||
logger.Debug("Nighttime...");
|
||||
this.moddingContext.SaveSettingsData(this.modDefinition.Id, settings ?? new());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user