mirror of
https://github.com/rmroc451/TweaksAndThings.git
synced 2025-12-16 09:19:37 -06:00
2.0.0 feature updates
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<MajorVersion>1</MajorVersion>
|
<MajorVersion>2</MajorVersion>
|
||||||
<MinorVersion>2</MinorVersion>
|
<MinorVersion>0</MinorVersion>
|
||||||
<PatchVersion>7</PatchVersion>
|
<PatchVersion>0</PatchVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
using Game.State;
|
using Game.Notices;
|
||||||
|
using Game.State;
|
||||||
using Helpers;
|
using Helpers;
|
||||||
using Model.Ops;
|
using Model.Ops;
|
||||||
using Network;
|
using Network;
|
||||||
using RMROC451.TweaksAndThings.Extensions;
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
|
using RMROC451.TweaksAndThings.Patches;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Track;
|
||||||
using UI.Console;
|
using UI.Console;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Commands;
|
namespace RMROC451.TweaksAndThings.Commands;
|
||||||
|
|
||||||
@@ -37,8 +41,15 @@ public class EchoCommand : IConsoleCommand
|
|||||||
return "+ to include area or - to leave it out";
|
return "+ to include area or - to leave it out";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comps[2] == "+") message += $" {OpsController.Shared.ClosestArea(car)?.name ?? "???"}";
|
var gamePoint = car.GetCenterPosition(Graph._graph);
|
||||||
Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\"");
|
EntityReference entityReference = new EntityReference(EntityType.Position, new Vector4( gamePoint.x, gamePoint.y, gamePoint.z, 0));
|
||||||
|
EntityReference loco = new EntityReference(EntityType.Car, car.id);
|
||||||
|
if (comps[2] == "+") message = new Hyperlink(entityReference.URI(), string.Format(message, OpsController.Shared.ClosestArea(car)?.name ?? "???"));
|
||||||
|
|
||||||
|
car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}");
|
||||||
|
ExpandedConsole_Add_Patch.SendMs(null, $"{Hyperlink.To(car)} {message}");
|
||||||
|
Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\"");
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
7
TweaksAndThings/Enums/CrewHourLoadMethod.cs
Normal file
7
TweaksAndThings/Enums/CrewHourLoadMethod.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace RMROC451.TweaksAndThings.Enums;
|
||||||
|
|
||||||
|
public enum CrewHourLoadMethod
|
||||||
|
{
|
||||||
|
Tracks,
|
||||||
|
Daily
|
||||||
|
}
|
||||||
@@ -4,5 +4,6 @@ public enum MrocHelperType
|
|||||||
{
|
{
|
||||||
Handbrake,
|
Handbrake,
|
||||||
GladhandAndAnglecock,
|
GladhandAndAnglecock,
|
||||||
BleedAirSystem
|
BleedAirSystem,
|
||||||
|
Oil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Model.AI;
|
using Model.AI;
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using Random = UnityEngine.Random;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Extensions
|
namespace RMROC451.TweaksAndThings.Extensions
|
||||||
{
|
{
|
||||||
@@ -15,7 +17,7 @@ namespace RMROC451.TweaksAndThings.Extensions
|
|||||||
public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log, bool cabooseRequired)
|
public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log, bool cabooseRequired)
|
||||||
{
|
{
|
||||||
int originIndex = oiler.FindOriginIndex();
|
int originIndex = oiler.FindOriginIndex();
|
||||||
Model.Car? foundCaboose = oiler._cars.CabooseInConsist();
|
Model.Car? foundCaboose = oiler._originCar.FindMyCaboose(0.0f, false);
|
||||||
if (originIndex < 0)
|
if (originIndex < 0)
|
||||||
{
|
{
|
||||||
_log.Error("Couldn't find origin car {car}", oiler._originCar);
|
_log.Error("Couldn't find origin car {car}", oiler._originCar);
|
||||||
@@ -37,6 +39,7 @@ namespace RMROC451.TweaksAndThings.Extensions
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
yield return new WaitForSeconds(AutoOiler.StartDelay.CabooseHalvedFloat(foundCaboose));
|
yield return new WaitForSeconds(AutoOiler.StartDelay.CabooseHalvedFloat(foundCaboose));
|
||||||
|
foundCaboose = oiler._originCar.FindMyCaboose(0.0f,false);
|
||||||
int carIndex = originIndex;
|
int carIndex = originIndex;
|
||||||
float adjustedTimeToWalk = AutoOiler.TimeToWalkCar.CabooseHalvedFloat(foundCaboose);
|
float adjustedTimeToWalk = AutoOiler.TimeToWalkCar.CabooseHalvedFloat(foundCaboose);
|
||||||
do
|
do
|
||||||
@@ -58,7 +61,7 @@ namespace RMROC451.TweaksAndThings.Extensions
|
|||||||
if (car.HasHotbox && car.Oiled == 1f && cabooseRequired && foundCaboose)
|
if (car.HasHotbox && car.Oiled == 1f && cabooseRequired && foundCaboose)
|
||||||
{
|
{
|
||||||
_log.Information("AutoOiler {name}: {foundCaboose} repaired hotbox {car}", oiler.name, foundCaboose, car);
|
_log.Information("AutoOiler {name}: {foundCaboose} repaired hotbox {car}", oiler.name, foundCaboose, car);
|
||||||
car.AdjustHotboxValue(0f);
|
car.AdjustHotboxValue();
|
||||||
}
|
}
|
||||||
num += adjustedTimeToWalk;
|
num += adjustedTimeToWalk;
|
||||||
oiler._pendingRunDuration += adjustedTimeToWalk;
|
oiler._pendingRunDuration += adjustedTimeToWalk;
|
||||||
@@ -74,18 +77,18 @@ namespace RMROC451.TweaksAndThings.Extensions
|
|||||||
|
|
||||||
public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log, bool cabooseRequired)
|
public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log, bool cabooseRequired)
|
||||||
{
|
{
|
||||||
|
Func<Model.Car?> foundCaboose = () => spotter._locomotive.FindMyCaboose(0.0f, false);
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Model.Car? foundCaboose = spotter._cars.CabooseInConsist();
|
|
||||||
if (!spotter.HasCars)
|
if (!spotter.HasCars)
|
||||||
{
|
{
|
||||||
yield return new WaitForSeconds(1f);
|
yield return new WaitForSeconds(1f);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
var fc = foundCaboose();
|
||||||
_log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Found Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}",
|
_log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Found Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}",
|
||||||
spotter.name, foundCaboose, spotter.HasCars, cabooseRequired);
|
spotter.name, fc, spotter.HasCars, cabooseRequired);
|
||||||
foundCaboose = spotter._cars.CabooseInConsist();
|
if (CabooseRequirementChecker(string.Format("{0} {1}", spotter.GetType().Name, spotter.name), cabooseRequired, fc, _log))
|
||||||
if (CabooseRequirementChecker(string.Format("{0} {1}", spotter.GetType().Name, spotter.name), cabooseRequired, foundCaboose, _log))
|
|
||||||
{
|
{
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
@@ -93,12 +96,12 @@ namespace RMROC451.TweaksAndThings.Extensions
|
|||||||
while (spotter.HasCars)
|
while (spotter.HasCars)
|
||||||
{
|
{
|
||||||
int num = Random.Range(60, 300);
|
int num = Random.Range(60, 300);
|
||||||
foundCaboose = spotter._cars.CabooseInConsist();
|
fc = foundCaboose();
|
||||||
if (foundCaboose)
|
if (fc)
|
||||||
{
|
{
|
||||||
var numOrig = num;
|
var numOrig = num;
|
||||||
num = Random.Range(15, 30);
|
num = Random.Range(15, 30);
|
||||||
_log.Information("AutoHotboxSpotter {name}: Next check went from num(60,300) => {numOrig}; to num(15,30) => {hasCaboose}; Requires Caboose {requiresCaboose}", spotter.name, numOrig, num, foundCaboose, cabooseRequired);
|
_log.Information("AutoHotboxSpotter {name}: Next check went from num(60,300) => {numOrig}; to num(15,30) => {hasCaboose}; Requires Caboose {requiresCaboose}", spotter.name, numOrig, num, fc, cabooseRequired);
|
||||||
}
|
}
|
||||||
yield return new WaitForSeconds(num);
|
yield return new WaitForSeconds(num);
|
||||||
spotter.CheckForHotbox();
|
spotter.CheckForHotbox();
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
using Game.Messages;
|
using Game.Messages;
|
||||||
using Game.State;
|
|
||||||
using Helpers;
|
using Helpers;
|
||||||
using Model;
|
using Model;
|
||||||
using Model.Definition;
|
using Model.Definition;
|
||||||
using Model.Definition.Data;
|
using Model.Definition.Data;
|
||||||
using Model.Ops;
|
using Model.Ops;
|
||||||
|
using Model.Ops.Timetable;
|
||||||
|
using Railloader;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.AccessControl;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Extensions;
|
namespace RMROC451.TweaksAndThings.Extensions;
|
||||||
@@ -53,7 +53,7 @@ public static class Car_Extensions
|
|||||||
return car;
|
return car;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool NotMotivePower(this Car car) => car is not BaseLocomotive && car.Archetype != Model.Definition.CarArchetype.Tender;
|
public static bool MotivePower(this Car car) => car is BaseLocomotive || car.Archetype == Model.Definition.CarArchetype.Tender;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For every car in the consist, cost 1 minute of AI Engineer time.
|
/// For every car in the consist, cost 1 minute of AI Engineer time.
|
||||||
@@ -66,18 +66,42 @@ public static class Car_Extensions
|
|||||||
|
|
||||||
public static bool IsCabooseAndStoppedForLoadRefresh(this Car car, bool isFull) => car.IsCaboose() && car.IsStopped(30f) && !isFull;
|
public static bool IsCabooseAndStoppedForLoadRefresh(this Car car, bool isFull) => car.IsCaboose() && car.IsStopped(30f) && !isFull;
|
||||||
|
|
||||||
public static Car? CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(c => c.IsCaboose());
|
public static Car? CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(IsCaboose);
|
||||||
|
|
||||||
public static bool ConsistNoFreight(this IEnumerable<Car> input) =>
|
public static bool ConsistNoFreight(this IEnumerable<Car> input) =>
|
||||||
input.Where(c => !c.IsLocomotive).Any() &&
|
input.Where(c => !c.MotivePower()).Any() &&
|
||||||
input
|
input
|
||||||
.Where(c => !c.IsLocomotive)
|
.Where(c => !c.MotivePower())
|
||||||
.All(c => !c.Archetype.IsFreight());
|
.All(c => !c.MrocIsFreight());
|
||||||
|
|
||||||
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, HashSet<string> carIdsCheckedAlready, bool decrement = false)
|
public static bool ConsistFreight(this IEnumerable<Car> input) =>
|
||||||
|
input.Where(c => !c.MotivePower()).Any() &&
|
||||||
|
input
|
||||||
|
.Where(c => !c.MotivePower())
|
||||||
|
.All(c => c.MrocIsFreight());
|
||||||
|
|
||||||
|
public static bool MrocIsFreight(this Car c) =>
|
||||||
|
c.Archetype.IsFreight() || c.Archetype switch
|
||||||
|
{
|
||||||
|
CarArchetype.LocomotiveDiesel => false,
|
||||||
|
CarArchetype.LocomotiveSteam => false,
|
||||||
|
CarArchetype.Coach => false,
|
||||||
|
CarArchetype.Baggage => false,
|
||||||
|
_ => true
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static bool SelectedEngineExpress(this TrainController input) =>
|
||||||
|
input.SelectedLocomotive.TryGetTimetableTrain(out Timetable.Train t) &&
|
||||||
|
t.TrainClass == Timetable.TrainClass.First;
|
||||||
|
|
||||||
|
public static Car? FindMyCaboose(this Car car, float timeNeeded, bool decrement = false) =>
|
||||||
|
car.CarsNearCurrentCar(timeNeeded, decrement).FindNearestCabooseFromNearbyCars();
|
||||||
|
|
||||||
|
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, bool decrement = false)
|
||||||
{
|
{
|
||||||
Car? output = null;
|
Car? output = null;
|
||||||
if (carIdsCheckedAlready.Contains(car.id) || !car.IsCaboose()) return null;
|
if (!car.IsCaboose()) return null;
|
||||||
|
|
||||||
List<LoadSlot> loadSlots = car.Definition.LoadSlots;
|
List<LoadSlot> loadSlots = car.Definition.LoadSlots;
|
||||||
for (int i = 0; i < loadSlots.Count; i++)
|
for (int i = 0; i < loadSlots.Count; i++)
|
||||||
@@ -94,51 +118,49 @@ public static class Car_Extensions
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Car? HuntingForCabeeseNearCar(this Car car, float timeNeeded, TrainController tc, HashSet<string> carIdsCheckedAlready, bool decrement = false)
|
private static Car FindNearestCabooseFromNearbyCars(this List<(Car car, bool crewCar, float distance)> source) =>
|
||||||
|
source
|
||||||
|
.OrderBy(c => c.crewCar ? 0 : 1)
|
||||||
|
.ThenBy(c => c.distance)
|
||||||
|
.Select(c => c.car)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
private static List<(Car car, bool crewCar, float distance)> CarsNearCurrentCar(this Car car, float timeNeeded, bool decrement)
|
||||||
{
|
{
|
||||||
List<(string carId, float distance)> source =
|
|
||||||
CarsNearCurrentCar(car, timeNeeded, tc, carIdsCheckedAlready, decrement);
|
|
||||||
|
|
||||||
Car output = FindNearestCabooseFromNearbyCars(tc, source);
|
var cabeese =
|
||||||
if (output != null) output.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement);
|
car.EnumerateCoupled().SelectMany(consistCar =>
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Car FindNearestCabooseFromNearbyCars(TrainController tc, List<(string carId, float distance)> source) =>
|
|
||||||
(
|
|
||||||
from t in source
|
|
||||||
where t.distance < 21f //todo: add setting slider for catchment
|
|
||||||
orderby t.distance ascending
|
|
||||||
select tc.CarForId(t.carId)
|
|
||||||
).FirstOrDefault();
|
|
||||||
|
|
||||||
private static List<(string carId, float distance)> CarsNearCurrentCar(Car car, float timeNeeded, TrainController tc, HashSet<string> carIdsCheckedAlready, bool decrement)
|
|
||||||
{
|
|
||||||
Vector3 position = car.GetMotionSnapshot().Position;
|
|
||||||
Vector3 center = WorldTransformer.WorldToGame(position);
|
|
||||||
var cars = tc.CarIdsInRadius(center, 60f);
|
|
||||||
Log.Information($"{nameof(HuntingForCabeeseNearCar)} => {cars.Count()}");
|
|
||||||
List<(string carId, float distance)> source =
|
|
||||||
cars
|
|
||||||
.Select(carId =>
|
|
||||||
{
|
{
|
||||||
Car car = tc.CarForId(carId);
|
Vector3 position = consistCar.GetMotionSnapshot().Position;
|
||||||
if (car == null || !car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready))
|
Vector3 center = WorldTransformer.WorldToGame(position);
|
||||||
{
|
var o = TrainController.Shared
|
||||||
return (carId: carId, distance: 1000f);
|
.CarIdsInRadius(center, SingletonPluginBase<TweaksAndThingsPlugin>.Shared.CabeeseSearchRadiusInMeters())
|
||||||
}
|
.Where(c => TrainController.Shared.CarForId(c).IsCaboose());
|
||||||
Vector3 a = WorldTransformer.WorldToGame(car.GetMotionSnapshot().Position);
|
return o;
|
||||||
return (carId: carId, distance: Vector3.Distance(a, center));
|
}).Distinct().Select(c => TrainController.Shared.CarForId(c));
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
|
Log.Information($"{nameof(CarsNearCurrentCar)} => {cabeese.Count()}");
|
||||||
|
|
||||||
|
List<(Car car, bool crewCar, float distance)> source =
|
||||||
|
cabeese.Select(c => (car: c, crewCar: c.IsCrewCar(), distance: car.Distance(c))).ToList();
|
||||||
|
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AdjustHotboxValue(this Car car, float hotboxValue) =>
|
public static float Distance(this Car car1, Car car2)
|
||||||
StateManager.ApplyLocal(
|
{
|
||||||
new PropertyChange(
|
Vector3 position = car1.GetMotionSnapshot().Position;
|
||||||
car.id, PropertyChange.KeyForControl(PropertyChange.Control.Hotbox),
|
Vector3 center = WorldTransformer.WorldToGame(position);
|
||||||
new FloatPropertyValue(hotboxValue)
|
Vector3 a = WorldTransformer.WorldToGame(car2.GetMotionSnapshot().Position);
|
||||||
)
|
|
||||||
);
|
return Vector3.Distance(a, center);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsCrewCar(this Car car) =>
|
||||||
|
!string.IsNullOrEmpty(TrainController.Shared.SelectedLocomotive.trainCrewId) &&
|
||||||
|
car.trainCrewId == TrainController.Shared.SelectedLocomotive.trainCrewId;
|
||||||
|
|
||||||
|
|
||||||
|
public static void AdjustHotboxValue(this Car car) => car.ControlProperties[PropertyChange.Control.Hotbox] = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace RMROC451.TweaksAndThings.Extensions;
|
|||||||
|
|
||||||
public static class TextSprite_Extensions
|
public static class TextSprite_Extensions
|
||||||
{
|
{
|
||||||
public static string TriColorPiePercent(this float quantity, float capacity)
|
public static string TriColorPiePercent(this float quantity, float capacity, string name = "")
|
||||||
{
|
{
|
||||||
int num;
|
int num;
|
||||||
if (capacity <= 0f)
|
if (capacity <= 0f)
|
||||||
@@ -29,6 +29,8 @@ public static class TextSprite_Extensions
|
|||||||
color = "#D53427"; //Red
|
color = "#D53427"; //Red
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"<sprite tint=1 color={color} name=Pie{num:D2}>";
|
string sprite = string.IsNullOrEmpty(name) ? $"Pie{num:D2}" : name;
|
||||||
|
|
||||||
|
return $"<sprite tint=1 color={color} name={sprite}>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Railloader;
|
||||||
|
using UI.Builder;
|
||||||
|
using UI.EngineControls;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(AutoEngineerControlSetBase))]
|
||||||
|
[HarmonyPatch(nameof(AutoEngineerControlSetBase.UpdateStatusLabel))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class AutoEngineerControlSetBase_UpdateStatusLabel_Patch
|
||||||
|
{
|
||||||
|
static void Postfix(AutoEngineerControlSetBase __instance)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!tweaksAndThings.IsEnabled() || !AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.SafetyFirstGoverningApplies()) return;
|
||||||
|
|
||||||
|
string orig = __instance.statusLabel.text;
|
||||||
|
__instance.statusLabel.text = $"{orig}; <b>Safety</b>";
|
||||||
|
__instance.statusLabel.rectTransform.Tooltip("Status", $"<b>Safety First, Speed Limited</b>\n\n{orig}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
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.Information($"{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);
|
||||||
|
|
||||||
|
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.Information(logMessage);
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Game.Notices;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Model;
|
||||||
|
using Serilog;
|
||||||
|
using UI.EngineControls;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(AutoEngineerOrdersHelper))]
|
||||||
|
[HarmonyPatch(nameof(AutoEngineerOrdersHelper.SetWaypoint), typeof(Track.Location), typeof(string))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class AutoEngineerOrdersHelper_SetWaypoint_patch
|
||||||
|
{
|
||||||
|
private static Serilog.ILogger _log => Log.ForContext<AutoEngineerOrdersHelper_SetWaypoint_patch>();
|
||||||
|
static void Postfix(AutoEngineerOrdersHelper __instance, Track.Location location, string coupleToCarId)
|
||||||
|
{
|
||||||
|
_log.Information($"start setWP");
|
||||||
|
Car selectedLoco = __instance._locomotive;
|
||||||
|
_log.Information($"{selectedLoco?.DisplayName ?? ""} set WP");
|
||||||
|
Vector3 gamePoint = location.GetPosition();
|
||||||
|
EntityReference entityReference = new EntityReference(EntityType.Position, new Vector4(gamePoint.x, gamePoint.y, gamePoint.z, 0));
|
||||||
|
selectedLoco.PostNotice("ai-wpt-rmroc451", new Hyperlink(entityReference.URI(), $"WP SET"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ internal class AutoHotboxSpotter_SpotterLoop_Patch
|
|||||||
public static bool Prefix(AutoHotboxSpotter __instance, ref IEnumerator __result)
|
public static bool Prefix(AutoHotboxSpotter __instance, ref IEnumerator __result)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||||
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
|
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
|
||||||
|
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ internal class AutoOiler_Loop_Patch
|
|||||||
public static bool Prefix(AutoOiler __instance, ref IEnumerator __result)
|
public static bool Prefix(AutoOiler __instance, ref IEnumerator __result)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||||
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
|
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() && !__instance._cars.ConsistNoFreight();
|
||||||
|
|
||||||
if (buttonsHaveCost) __result = __instance.MrocAutoOilerLoop(_log, cabooseRequired);
|
if (buttonsHaveCost) __result = __instance.MrocAutoOilerLoop(_log, cabooseRequired);
|
||||||
return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
|
return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ 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.EndGearHelpersRequirePayment();
|
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||||
|
|
||||||
var consist = __instance._car.EnumerateCoupled();
|
var consist = __instance._car.EnumerateCoupled();
|
||||||
@@ -82,6 +82,7 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
{
|
{
|
||||||
builder.HStack(delegate (UIPanelBuilder hstack)
|
builder.HStack(delegate (UIPanelBuilder hstack)
|
||||||
{
|
{
|
||||||
|
hstack = AddCarConsistRebuildObservers(hstack, consist, all: false);
|
||||||
hstack.AddField("Consist Info", hstack.HStack(delegate (UIPanelBuilder field)
|
hstack.AddField("Consist Info", hstack.HStack(delegate (UIPanelBuilder field)
|
||||||
{
|
{
|
||||||
int consistLength = consist.Count();
|
int consistLength = consist.Count();
|
||||||
@@ -96,7 +97,7 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist)
|
private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist, bool all = true)
|
||||||
{
|
{
|
||||||
TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>();
|
TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>();
|
||||||
foreach (Model.Car car in consist.Where(c => c.Archetype != Model.Definition.CarArchetype.Tender))
|
foreach (Model.Car car in consist.Where(c => c.Archetype != Model.Definition.CarArchetype.Tender))
|
||||||
@@ -105,8 +106,8 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
foreach (LogicalEnd logicalEnd in ends)
|
foreach (LogicalEnd logicalEnd in ends)
|
||||||
{
|
{
|
||||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsCoupled, car.LogicalToEnd(logicalEnd)), tagController);
|
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsCoupled, car.LogicalToEnd(logicalEnd)), tagController);
|
||||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsAirConnected, car.LogicalToEnd(logicalEnd)), tagController);
|
if (all) builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsAirConnected, car.LogicalToEnd(logicalEnd)), tagController);
|
||||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.Anglecock, car.LogicalToEnd(logicalEnd)), tagController);
|
if (all) builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.Anglecock, car.LogicalToEnd(logicalEnd)), tagController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,8 +149,9 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost)
|
public static int MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost)
|
||||||
{
|
{
|
||||||
|
int output = 0;
|
||||||
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
|
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
|
||||||
IEnumerable<Model.Car> consist = car.EnumerateCoupled();
|
IEnumerable<Model.Car> consist = car.EnumerateCoupled();
|
||||||
_log.ForContext("car", car).Verbose($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
_log.ForContext("car", car).Verbose($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
||||||
@@ -178,14 +180,26 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MrocHelperType.BleedAirSystem:
|
case MrocHelperType.BleedAirSystem:
|
||||||
consist = consist.Where(c => c.NotMotivePower());
|
consist = consist.Where(c => !c.MotivePower());
|
||||||
_log.ForContext("car", car).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));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MrocHelperType.Oil:
|
||||||
|
consist = consist.Where(c => c.NeedsOiling || c.HasHotbox);
|
||||||
|
_log.ForContext("car", car).Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
||||||
|
foreach (Model.Car oil in consist)
|
||||||
|
{
|
||||||
|
StateManager.ApplyLocal(new PropertyChange(oil.id, nameof(Car.Oiled).ToLower(), new FloatPropertyValue(1)));
|
||||||
|
output += car.HasHotbox ? 1 : 0;
|
||||||
|
if (car.HasHotbox) car.AdjustHotboxValue();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void CarEndAirUpdate(Car c)
|
internal static void CarEndAirUpdate(Car c)
|
||||||
@@ -214,7 +228,7 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
float timeCost = originalTimeCost;
|
float timeCost = originalTimeCost;
|
||||||
float crewCost = timeCost / 3600; //hours of time deducted from caboose.
|
float crewCost = timeCost / 3600; //hours of time deducted from caboose.
|
||||||
var tsString = crewCost.FormatCrewHours(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad().description);
|
var tsString = crewCost.FormatCrewHours(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad().description);
|
||||||
Car? cabooseWithAvailCrew = NearbyCabooseWithAvailableCrew(car, crewCost, buttonsHaveCost);
|
Car? cabooseWithAvailCrew = car.FindMyCaboose(crewCost, buttonsHaveCost);
|
||||||
if (cabooseWithAvailCrew == null) timeCost *= 1.5f;
|
if (cabooseWithAvailCrew == null) timeCost *= 1.5f;
|
||||||
var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose";
|
var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose";
|
||||||
|
|
||||||
@@ -226,30 +240,4 @@ internal class CarInspector_PopulateCarPanel_Patch
|
|||||||
StateManager_OnDayDidChange_Patch.UnbilledAutoBrakeCrewRunDuration += timeCost;
|
StateManager_OnDayDidChange_Patch.UnbilledAutoBrakeCrewRunDuration += timeCost;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Car? NearbyCabooseWithAvailableCrew(Car car, float timeNeeded, bool decrement = false)
|
|
||||||
{
|
|
||||||
HashSet<string> carIdsCheckedAlready = new();
|
|
||||||
|
|
||||||
//check current car.
|
|
||||||
Car? output = car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement);
|
|
||||||
if (output != null) return output; //short out if we are good
|
|
||||||
carIdsCheckedAlready.Add(car.id);
|
|
||||||
|
|
||||||
//check consist, for cabeese
|
|
||||||
IEnumerable<Car> consist = car.EnumerateCoupled();
|
|
||||||
output = consist.FirstOrDefault(c => c.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement));
|
|
||||||
if (output != null) return output; //short out if we are good
|
|
||||||
carIdsCheckedAlready.UnionWith(consist.Select(c => c.id));
|
|
||||||
|
|
||||||
//then check near consist cars for cabeese
|
|
||||||
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
|
|
||||||
foreach (var c in consist)
|
|
||||||
{
|
|
||||||
output = c.HuntingForCabeeseNearCar(timeNeeded, tc, carIdsCheckedAlready, decrement);
|
|
||||||
if (output != null) return output; //short out if we are good
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using Core;
|
using Core;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Model;
|
using Model;
|
||||||
|
using Model.Ops;
|
||||||
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 RollingStock;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.Remoting.Messaging;
|
|
||||||
using UI;
|
using UI;
|
||||||
|
using UI.Tags;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
||||||
@@ -37,12 +39,11 @@ internal class CarPickable_Activate_Patch
|
|||||||
private static bool Prefix(CarPickable __instance, PickableActivateEvent evt)
|
private static bool Prefix(CarPickable __instance, PickableActivateEvent evt)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
bool bCtrlAltHeld = GameInput.IsControlDown && GameInput.IsAltDown;
|
bool bCtrlAltHeld = GameInput.IsControlDown && GameInput.IsAltDown;
|
||||||
|
|
||||||
_log.ForContext("car", __instance.car).Information($"{GameInput.IsShiftDown} {GameInput.IsControlDown} {GameInput.IsAltDown} {bCtrlAltHeld} ");
|
_log.ForContext("car", __instance.car).Information($"{GameInput.IsShiftDown} {GameInput.IsControlDown} {GameInput.IsAltDown} {bCtrlAltHeld} ");
|
||||||
|
|
||||||
//On Double click of a car: follow
|
|
||||||
if (OnPointerDown(evt, PickableActivation.Primary))
|
if (OnPointerDown(evt, PickableActivation.Primary))
|
||||||
{
|
{
|
||||||
CameraSelector.shared.FollowCar(__instance.car);
|
CameraSelector.shared.FollowCar(__instance.car);
|
||||||
@@ -57,27 +58,29 @@ internal class CarPickable_Activate_Patch
|
|||||||
var consist = __instance.car.EnumerateCoupled();
|
var consist = __instance.car.EnumerateCoupled();
|
||||||
bool handbrakesApplied = consist.Any(c => c.HandbrakeApplied());
|
bool handbrakesApplied = consist.Any(c => c.HandbrakeApplied());
|
||||||
bool airSystemIssues = consist.Any(c => c.EndAirSystemIssue());
|
bool airSystemIssues = consist.Any(c => c.EndAirSystemIssue());
|
||||||
bool needsOiling = GameInput.IsShiftDown && consist.All(c => c.IsStopped()) && consist.Any(c => c.NeedsOiling || c.HasHotbox) && (consist.CabooseInConsist() || !tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter());
|
Func<bool> cabooseNear = () => (bool)__instance.car.FindMyCaboose(0.0f, false);
|
||||||
|
bool needsOiling = GameInput.IsShiftDown && consist.All(c => c.IsStopped()) && consist.Any(c => c.NeedsOiling || c.HasHotbox) && (!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || cabooseNear());
|
||||||
var chargeIt = handbrakesApplied || airSystemIssues || needsOiling;
|
var chargeIt = handbrakesApplied || airSystemIssues || needsOiling;
|
||||||
//CTRL + ALT + SHIFT : BrakesAngleCocksAndOiling
|
//CTRL + ALT + SHIFT : BrakesAngleCocksAndOiling
|
||||||
//CTRL + ALT : Release Consist Brakes and Check AngleCocks
|
//CTRL + ALT : Release Consist Brakes and Check AngleCocks
|
||||||
//CTRL + SHIFT : Toggle Consist Brakes
|
//ALT + SHIFT : toggle consist brakes
|
||||||
//CTRL : Toggle Car Brakes & Airup Car
|
//CTRL + SHIFT : Check Consist Angle Cocks
|
||||||
//ALT + SHIFT : Check Consist AngleCocks
|
//ALT : Toggle car brakes and & air up cars
|
||||||
//ALT : Open Inspector to Car
|
//CTRL : NOTHING; BASE CAR INSPECTOR
|
||||||
|
|
||||||
if (bCtrlAltHeld)
|
if (bCtrlAltHeld)
|
||||||
{
|
{
|
||||||
BrakesAngleCocksAndOiling(__instance, tweaksAndThings, GameInput.IsShiftDown, consist, handbrakesApplied, airSystemIssues, needsOiling, chargeIt);
|
BrakesAngleCocksAndOiling(__instance, tweaksAndThings, GameInput.IsShiftDown, consist, handbrakesApplied, airSystemIssues, needsOiling, chargeIt);
|
||||||
_log.ForContext("car", __instance.car).Information($"ctrlAlt{(GameInput.IsShiftDown ? "shift" : string.Empty)}Held!");
|
_log.ForContext("car", __instance.car).Information($"ctrlAlt{(GameInput.IsShiftDown ? "shift" : string.Empty)}Held!");
|
||||||
output = false;
|
output = false;
|
||||||
}
|
}
|
||||||
else if (GameInput.IsControlDown && GameInput.IsShiftDown)
|
else if (GameInput.IsAltDown && GameInput.IsShiftDown)
|
||||||
{
|
{
|
||||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment());
|
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment());
|
||||||
_log.ForContext("car", __instance.car).Information("ctrlShiftHeld!");
|
_log.ForContext("car", __instance.car).Information("ctrlShiftHeld!");
|
||||||
output = false;
|
output = false;
|
||||||
}
|
}
|
||||||
else if (GameInput.IsAltDown && GameInput.IsShiftDown)
|
else if (GameInput.IsControlDown && GameInput.IsShiftDown)
|
||||||
{
|
{
|
||||||
if (airSystemIssues)
|
if (airSystemIssues)
|
||||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.GladhandAndAnglecock, tweaksAndThings.EndGearHelpersRequirePayment());
|
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.GladhandAndAnglecock, tweaksAndThings.EndGearHelpersRequirePayment());
|
||||||
@@ -85,16 +88,14 @@ internal class CarPickable_Activate_Patch
|
|||||||
output = false;
|
output = false;
|
||||||
}
|
}
|
||||||
else if (GameInput.IsAltDown)
|
else if (GameInput.IsAltDown)
|
||||||
{
|
|
||||||
UI.CarInspector.CarInspector.Show(__instance.car);
|
|
||||||
_log.ForContext("car", __instance.car).Information("altHeld!");
|
|
||||||
output = false;
|
|
||||||
}
|
|
||||||
else if (GameInput.IsControlDown)
|
|
||||||
{
|
{
|
||||||
__instance.car.SetHandbrake(!__instance.car.HandbrakeApplied());
|
__instance.car.SetHandbrake(!__instance.car.HandbrakeApplied());
|
||||||
CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(__instance.car);
|
CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(__instance.car);
|
||||||
|
if (__instance.car.TryGetAdjacentCar(Car.LogicalEnd.A, out Model.Car cA)) CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(cA);
|
||||||
|
if (__instance.car.TryGetAdjacentCar(Car.LogicalEnd.B, out Model.Car cB)) CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(cB);
|
||||||
_log.ForContext("car", __instance.car).Information("ctrlHeld!");
|
_log.ForContext("car", __instance.car).Information("ctrlHeld!");
|
||||||
|
TagController.Shared.UpdateTag(__instance.car, __instance.car.TagCallout, OpsController.Shared);
|
||||||
|
__instance.car.TagCallout.Update();
|
||||||
output = false;
|
output = false;
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
@@ -120,18 +121,9 @@ internal class CarPickable_Activate_Patch
|
|||||||
if (airSystemIssues)
|
if (airSystemIssues)
|
||||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.GladhandAndAnglecock, false);
|
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.GladhandAndAnglecock, false);
|
||||||
if (needsOiling)
|
if (needsOiling)
|
||||||
{
|
hbFix = CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.Oil, false);
|
||||||
foreach (var car in consist.Where(c => c.NeedsOiling || c.HasHotbox))
|
|
||||||
{
|
|
||||||
float num2 = 1f - car.Oiled;
|
|
||||||
car.OffsetOiled(num2);
|
|
||||||
hbFix += car.HasHotbox ? 1 : 0;
|
|
||||||
if (car.HasHotbox) car.AdjustHotboxValue(0f);
|
|
||||||
}
|
|
||||||
if (hbFix > 0)
|
if (hbFix > 0)
|
||||||
Multiplayer.Broadcast($"Near {Hyperlink.To(__instance.car)}: \"{hbFix.Pluralize("hotbox") + " repaired!"}\"");
|
Multiplayer.Broadcast($"Near {Hyperlink.To(__instance.car)}: \"{hbFix.Pluralize("hotbox") + " repaired!"}\"");
|
||||||
}
|
|
||||||
|
|
||||||
if (chargeIt)
|
if (chargeIt)
|
||||||
CarInspector_PopulateCarPanel_Patch.CalculateCostIfEnabled(__instance.car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment(), consist);
|
CarInspector_PopulateCarPanel_Patch.CalculateCostIfEnabled(__instance.car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment(), consist);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,31 +15,58 @@ namespace RMROC451.TweaksAndThings.Patches;
|
|||||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
internal class CarPickable_HandleShowContextMenu_Patch
|
internal class CarPickable_HandleShowContextMenu_Patch
|
||||||
{
|
{
|
||||||
private static void Postfix(Car car)
|
private static bool Prefix(Car car)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
|
|
||||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||||
|
TrainController trainController = TrainController.Shared;
|
||||||
ContextMenu shared = ContextMenu.Shared;
|
ContextMenu shared = ContextMenu.Shared;
|
||||||
shared.AddButton(ContextMenuQuadrant.Unused1, $"{(car.EnumerateCoupled().Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate
|
if (ContextMenu.IsShown)
|
||||||
|
{
|
||||||
|
shared.Hide();
|
||||||
|
}
|
||||||
|
shared.Clear();
|
||||||
|
|
||||||
|
shared.AddButton(ContextMenuQuadrant.General, (trainController.SelectedCar == car) ? "Deselect" : "Select", SpriteName.Select, delegate
|
||||||
|
{
|
||||||
|
trainController.SelectedCar = ((trainController.SelectedCar == car) ? null : car);
|
||||||
|
});
|
||||||
|
if (GameInput.IsShiftDown)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (!car.EnumerateCoupled().Any(c => !c.SupportsBleed()))
|
||||||
|
{
|
||||||
|
shared.AddButton(ContextMenuQuadrant.Brakes, $"Bleed Consist", SpriteName.Bleed, delegate
|
||||||
|
{
|
||||||
|
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
shared.AddButton(ContextMenuQuadrant.Brakes, $"{(car.EnumerateCoupled().Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate
|
||||||
{
|
{
|
||||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost);
|
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (car.EnumerateCoupled().Any(c => c.EndAirSystemIssue()))
|
if (car.EnumerateCoupled().Any(c => c.EndAirSystemIssue()))
|
||||||
{
|
{
|
||||||
shared.AddButton(ContextMenuQuadrant.Unused1, $"Air Up Consist", SpriteName.Select, delegate
|
shared.AddButton(ContextMenuQuadrant.Unused2, $"Air Up Consist", SpriteName.Select, delegate
|
||||||
{
|
{
|
||||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
|
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (!car.EnumerateCoupled().Any(c => !c.SupportsBleed()))
|
else {
|
||||||
|
if (car.SupportsBleed())
|
||||||
{
|
{
|
||||||
shared.AddButton(ContextMenuQuadrant.Unused2, $"Bleed Consist", SpriteName.Bleed, delegate
|
shared.AddButton(ContextMenuQuadrant.Brakes, "Bleed", SpriteName.Bleed, car.SetBleed);
|
||||||
|
}
|
||||||
|
shared.AddButton(ContextMenuQuadrant.Brakes, car.air.handbrakeApplied ? "Release Handbrake" : "Apply Handbrake", SpriteName.Handbrake, delegate
|
||||||
{
|
{
|
||||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
|
bool apply = !car.air.handbrakeApplied;
|
||||||
|
car.SetHandbrake(apply);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +74,10 @@ internal class CarPickable_HandleShowContextMenu_Patch
|
|||||||
{
|
{
|
||||||
CameraSelector.shared.FollowCar(car);
|
CameraSelector.shared.FollowCar(car);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
shared.Show(car.DisplayName);
|
||||||
shared.BuildItemAngles();
|
shared.BuildItemAngles();
|
||||||
shared.StartCoroutine(shared.AnimateButtonsShown());
|
shared.StartCoroutine(shared.AnimateButtonsShown());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
using HarmonyLib;
|
|
||||||
using Railloader;
|
|
||||||
using System;
|
|
||||||
using UI.ContextMenu;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Patches;
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(UI.ContextMenu.ContextMenu))]
|
|
||||||
//ContextMenuQuadrant quadrant, int index, float normalizedRadius = 1f
|
|
||||||
[HarmonyPatch(nameof(UI.ContextMenu.ContextMenu.PositionForItem), typeof(ContextMenuQuadrant), typeof(int), typeof(float))]
|
|
||||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
|
||||||
internal class ContextMenu_PositionForItem_Patch
|
|
||||||
{
|
|
||||||
static void Postfix(UI.ContextMenu.ContextMenu __instance, ref Vector2 __result, ContextMenuQuadrant quadrant, int index, float normalizedRadius = 1f)
|
|
||||||
{
|
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
|
||||||
if (!tweaksAndThings.IsEnabled) return;
|
|
||||||
|
|
||||||
float num = __instance._itemAngles[(quadrant, index)] * ((float)Math.PI / 180f);
|
|
||||||
float num2 = __instance.radius * normalizedRadius;
|
|
||||||
__result = new Vector2(1.3f * Mathf.Sin(num) * num2, Mathf.Cos(num) * num2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,7 @@ internal class ContextMenu_Show_Patch
|
|||||||
static bool Prefix(UI.ContextMenu.ContextMenu __instance, string centerText)
|
static bool Prefix(UI.ContextMenu.ContextMenu __instance, string centerText)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
|
|
||||||
if (!__instance.GetRootCanvas(out var rootCanvas))
|
if (!__instance.GetRootCanvas(out var rootCanvas))
|
||||||
{
|
{
|
||||||
@@ -77,7 +77,7 @@ internal class ContextMenu_DefaultAngleForItem_Patch
|
|||||||
static bool Prefix(UI.ContextMenu.ContextMenu __instance, ref float __result, ContextMenuQuadrant quadrant, int index, int quadrantItemCount)
|
static bool Prefix(UI.ContextMenu.ContextMenu __instance, ref float __result, ContextMenuQuadrant quadrant, int index, int quadrantItemCount)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
|
|
||||||
|
|
||||||
int num = quadrant switch
|
int num = quadrant switch
|
||||||
@@ -108,7 +108,7 @@ internal class ContextMenu_AddButton_Patch
|
|||||||
static bool Prefix(UI.ContextMenu.ContextMenu __instance, ContextMenuQuadrant quadrant, string title, Sprite sprite, Action action)
|
static bool Prefix(UI.ContextMenu.ContextMenu __instance, ContextMenuQuadrant quadrant, string title, Sprite sprite, Action action)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
|
|
||||||
|
|
||||||
List<ContextMenuItem> list = __instance._quadrants[(int)quadrant];
|
List<ContextMenuItem> list = __instance._quadrants[(int)quadrant];
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ internal class EngineRosterRow_Refresh_Patch
|
|||||||
|
|
||||||
if (tweaksAndThings == null ||
|
if (tweaksAndThings == null ||
|
||||||
rosterFuelColumnSettings == null ||
|
rosterFuelColumnSettings == null ||
|
||||||
!tweaksAndThings.IsEnabled ||
|
!tweaksAndThings.IsEnabled() ||
|
||||||
rosterFuelColumnSettings.EngineRosterFuelStatusColumn == EngineRosterFuelDisplayColumn.None || (!GameInput.IsAltDown && !rosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways))
|
rosterFuelColumnSettings.EngineRosterFuelStatusColumn == EngineRosterFuelDisplayColumn.None || (!GameInput.IsAltDown && !rosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -43,7 +43,10 @@ internal class EngineRosterRow_Refresh_Patch
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IEnumerable<Car> consist = __instance._engine.EnumerateCoupled().Where(c => c.EnableOiling);
|
IEnumerable<Car> consist = __instance._engine.EnumerateCoupled().Where(c => c.EnableOiling);
|
||||||
bool cabooseRequirementFulfilled = consist.CabooseInConsist() || !tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || consist.ConsistNoFreight();
|
bool cabooseRequirementFulfilled =
|
||||||
|
!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter()
|
||||||
|
|| consist.ConsistNoFreight()
|
||||||
|
|| (bool)__instance._engine.FindMyCaboose(0.0f, false);
|
||||||
float offendingPercentage = 0;
|
float offendingPercentage = 0;
|
||||||
Car engineOrTender = __instance._engine;
|
Car engineOrTender = __instance._engine;
|
||||||
List<LoadSlot> loadSlots = __instance._engine.Definition.LoadSlots;
|
List<LoadSlot> loadSlots = __instance._engine.Definition.LoadSlots;
|
||||||
|
|||||||
15
TweaksAndThings/Patches/EntityReference_Text_Patch.cs
Normal file
15
TweaksAndThings/Patches/EntityReference_Text_Patch.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(EntityReference))]
|
||||||
|
[HarmonyPatch(nameof(EntityReference.Text))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class EntityReference_Text_Patch
|
||||||
|
{
|
||||||
|
static void Postfix(ref string __result)
|
||||||
|
{
|
||||||
|
if (__result == "Unknown")
|
||||||
|
__result = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,13 +25,15 @@ internal class ExpandedConsole_Add_Patch
|
|||||||
{
|
{
|
||||||
entry.Text = $"{entry.Timestamp} : {entry.Text}";
|
entry.Text = $"{entry.Timestamp} : {entry.Text}";
|
||||||
entry.Timestamp = RealNow();
|
entry.Timestamp = RealNow();
|
||||||
SendMs(ref entry);
|
SendMs((UI.Console.Console.Entry?)entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SendMs(ref UI.Console.Console.Entry entry)
|
internal static void SendMs(UI.Console.Console.Entry? entry, string? text = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (entry is null && !String.IsNullOrEmpty(text)) entry = new() { Text = text };
|
||||||
|
var msgText = entry?.Text ?? string.Empty;
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
StateManager shared = StateManager.Shared;
|
StateManager shared = StateManager.Shared;
|
||||||
GameStorage gameStorage = shared.Storage;
|
GameStorage gameStorage = shared.Storage;
|
||||||
@@ -46,7 +48,7 @@ internal class ExpandedConsole_Add_Patch
|
|||||||
{
|
{
|
||||||
var t = new Regex("car:(.*)\"");
|
var t = new Regex("car:(.*)\"");
|
||||||
|
|
||||||
var carId = t.IsMatch(entry.Text) ? Regex.Match(entry.Text, "car:(.*?)\"").Groups[1].Captures[0].ToString() : string.Empty;
|
var carId = t.IsMatch(msgText) ? Regex.Match(msgText, "car:(.*?)\"").Groups[1].Captures[0].ToString() : string.Empty;
|
||||||
Model.Car? car = TrainController.Shared.CarForString(carId);
|
Model.Car? car = TrainController.Shared.CarForString(carId);
|
||||||
bool engineInMessage = car?.IsLocomotive ?? false;
|
bool engineInMessage = car?.IsLocomotive ?? false;
|
||||||
var image = engineInMessage ?
|
var image = engineInMessage ?
|
||||||
@@ -81,7 +83,7 @@ internal class ExpandedConsole_Add_Patch
|
|||||||
{
|
{
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
description= Regex.Replace(entry.Text, "<.*?>", "").Replace(": ", "\n"),
|
description= Regex.Replace(msgText, "<.*?>", "").Replace(": ", "\n"),
|
||||||
timestamp=DateTime.UtcNow,
|
timestamp=DateTime.UtcNow,
|
||||||
image
|
image
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
using Core;
|
||||||
|
using Game;
|
||||||
|
using Game.State;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Model.Ops;
|
||||||
|
using Network;
|
||||||
|
using Railloader;
|
||||||
|
using Serilog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(InterchangedIndustryLoader))]
|
||||||
|
[HarmonyPatch(nameof(InterchangedIndustryLoader.ServeInterchange), typeof(IIndustryContext), typeof(Interchange))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class InterchangedIndustryLoader_ServiceInterchange_Patch
|
||||||
|
{
|
||||||
|
private static Serilog.ILogger _log => Log.ForContext<InterchangedIndustryLoader_ServiceInterchange_Patch>();
|
||||||
|
/// <summary>
|
||||||
|
/// Allow Interchange service for buying parts/fuel when negative balance, adding an additional 20% penalty
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="__instance"></param>
|
||||||
|
/// <param name="ctx"></param>
|
||||||
|
/// <param name="interchange"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool Prefix(InterchangedIndustryLoader __instance, IIndustryContext ctx, Interchange interchange)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!StateManager.IsHost || !tweaksAndThings.IsEnabled() || !tweaksAndThings.ServiceFundPenalties()) return true;
|
||||||
|
|
||||||
|
StateManager shared = StateManager.Shared;
|
||||||
|
List<IOpsCar> list = (from car in EnumerateCars(__instance, ctx, requireWaybill: true)
|
||||||
|
where car.IsEmptyOrContains(__instance.load)
|
||||||
|
select car).ToList();
|
||||||
|
int num2 = 0;
|
||||||
|
int num3 = 0;
|
||||||
|
int penalty = 0;
|
||||||
|
GameDateTime returnTime = ctx.Now.AddingDays(23f / 24f);
|
||||||
|
foreach (IOpsCar item3 in list)
|
||||||
|
{
|
||||||
|
(float quantity, float capacity) tuple = item3.QuantityOfLoad(__instance.load);
|
||||||
|
float item = tuple.quantity;
|
||||||
|
float item2 = tuple.capacity;
|
||||||
|
int num4 = Mathf.RoundToInt((item2 - item) * __instance.load.costPerUnit);
|
||||||
|
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);
|
||||||
|
|
||||||
|
num2 += num4;
|
||||||
|
num3++;
|
||||||
|
}
|
||||||
|
item3.Load(__instance.load, item2);
|
||||||
|
item3.SetWaybill(null, __instance, "Full");
|
||||||
|
ctx.MoveToBardo(item3);
|
||||||
|
__instance.ScheduleReturnFromBardo(item3, returnTime);
|
||||||
|
}
|
||||||
|
if (num2 > 0)
|
||||||
|
{
|
||||||
|
__instance.Industry.ApplyToBalance(-num2, __instance.ledgerCategory, null, num3, quiet: true);
|
||||||
|
string penaltyText = penalty > 0 ? $"; Overdraft fee of {penalty:C0}" : string.Empty;
|
||||||
|
Multiplayer.Broadcast(string.Format("{6}: Ordered {0} of {1} at {2} for {3:C0}{5}. Expected return: {4}.", num3.Pluralize("car"), __instance.load.description, __instance.HyperlinkToThis, num2, 1.Pluralize("day"), penaltyText, __instance.HyperlinkToThis));
|
||||||
|
if (penalty > 0)
|
||||||
|
StateManager.Shared.ApplyToBalance(-penalty, __instance.ledgerCategory, null, $"Overdraft: {__instance.Industry.name}", 0, quiet: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<IOpsCar> EnumerateCars(InterchangedIndustryLoader __instance, IIndustryContext ctx, bool requireWaybill = false)
|
||||||
|
{
|
||||||
|
foreach (IOpsCar item in ctx.CarsAtPosition())
|
||||||
|
{
|
||||||
|
if (!CarTypeMatches(__instance.Interchange, item)) continue;
|
||||||
|
|
||||||
|
if (requireWaybill)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Waybill? waybill = item.Waybill;
|
||||||
|
string destId = waybill?.Destination.Identifier ?? string.Empty;
|
||||||
|
if (!waybill.HasValue || !destId.Equals(__instance.Identifier)) continue;
|
||||||
|
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error(ex, $"{item.DisplayName} issue detecting waybill");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CarTypeMatches(Interchange interchange, IOpsCar car)
|
||||||
|
{
|
||||||
|
string carType = car.CarType;
|
||||||
|
return interchange.carTypeFilter.Matches(carType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
183
TweaksAndThings/Patches/MapWindow_OnClick_Patch.cs
Normal file
183
TweaksAndThings/Patches/MapWindow_OnClick_Patch.cs
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Helpers;
|
||||||
|
using Map.Runtime;
|
||||||
|
using Model;
|
||||||
|
using Model.AI;
|
||||||
|
using Railloader;
|
||||||
|
using Serilog;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Track;
|
||||||
|
using UI;
|
||||||
|
using UI.EngineControls;
|
||||||
|
using UI.Map;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Pool;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using static UI.AutoEngineerDestinationPicker;
|
||||||
|
using Vector2 = UnityEngine.Vector2;
|
||||||
|
using Vector3 = UnityEngine.Vector3;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(MapWindow))]
|
||||||
|
[HarmonyPatch(nameof(MapWindow.OnClick), typeof(Vector2))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class MapWindow_OnClick_Patch
|
||||||
|
{
|
||||||
|
private static Serilog.ILogger _log => Log.ForContext<MapWindow_OnClick_Patch>();
|
||||||
|
private static GameObject _prefabHolder;
|
||||||
|
private static MapIcon _waypointPrefab;
|
||||||
|
private static Dictionary<string, MapIcon> wps = new Dictionary<string, MapIcon>();
|
||||||
|
|
||||||
|
public static Sprite? LoadTexture(string fileName, string name)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(SingletonPluginBase<TweaksAndThingsPlugin>.Shared.ModDirectory, fileName);
|
||||||
|
Texture2D texture2D = new Texture2D(128, 128, TextureFormat.DXT5, mipChain: false);
|
||||||
|
texture2D.name = name;
|
||||||
|
texture2D.wrapMode = TextureWrapMode.Clamp;
|
||||||
|
if (!ImageConversion.LoadImage(texture2D, File.ReadAllBytes(path)))
|
||||||
|
{
|
||||||
|
_log.Information($"Unable to load {name} icon!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f));
|
||||||
|
sprite.name = name;
|
||||||
|
|
||||||
|
//if (!SpriteLibrary.Shared.entries.Any(s => s.name.ToString() == name)) SpriteLibrary.Shared.entries.Add(new() { name = SpriteName.Meh, sprite = sprite });
|
||||||
|
return sprite;
|
||||||
|
}
|
||||||
|
public static MapIcon? waypointPrefab
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_waypointPrefab == null)
|
||||||
|
{
|
||||||
|
CreateWaypointPrefab();
|
||||||
|
}
|
||||||
|
return _waypointPrefab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static GameObject prefabHolder
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_prefabHolder == null)
|
||||||
|
{
|
||||||
|
_prefabHolder = new GameObject("Prefab Holder");
|
||||||
|
_prefabHolder.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
_prefabHolder.SetActive(value: false);
|
||||||
|
}
|
||||||
|
return _prefabHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateWaypointPrefab()
|
||||||
|
{
|
||||||
|
float num = 0.6f; //come back
|
||||||
|
Sprite sprite = LoadTexture("Map_pin_icon.png", "MapPin");
|
||||||
|
_waypointPrefab = Object.Instantiate(TrainController.Shared.locomotiveMapIconPrefab, prefabHolder.transform);
|
||||||
|
GameObject obj = _waypointPrefab.gameObject;
|
||||||
|
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
obj.name = "Map Waypoint Icon";
|
||||||
|
if ((bool)_waypointPrefab.Text)
|
||||||
|
{
|
||||||
|
Object.DestroyImmediate(_waypointPrefab.Text.gameObject);
|
||||||
|
}
|
||||||
|
Image componentInChildren = _waypointPrefab.GetComponentInChildren<Image>();
|
||||||
|
componentInChildren.sprite = sprite;
|
||||||
|
componentInChildren.transform.localScale = Vector3.one * num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Prefix(MapWindow __instance, Vector2 viewportNormalizedPoint)
|
||||||
|
{
|
||||||
|
if (GameInput.IsControlDown && GameInput.IsAltDown)
|
||||||
|
{
|
||||||
|
Ray ray = RayForViewportNormalizedPoint(__instance, viewportNormalizedPoint);
|
||||||
|
Vector3 gamePoint = MapManager.Instance.FindTerrainPointForXZ(WorldTransformer.WorldToGame(ray.origin));
|
||||||
|
var selectedLoco = TrainController.Shared?.SelectedLocomotive;
|
||||||
|
if (selectedLoco != null)
|
||||||
|
{
|
||||||
|
Hit? valueOrDefault = null;
|
||||||
|
Camera _camera = null;
|
||||||
|
if (MainCameraHelper.TryGetIfNeeded(ref _camera))
|
||||||
|
{
|
||||||
|
float rad = 200f;
|
||||||
|
if (Graph.Shared.TryGetLocationFromGamePoint(gamePoint, rad, out Location location))
|
||||||
|
{
|
||||||
|
Hit? hit = HitLocation(location, selectedLoco);
|
||||||
|
if (hit.HasValue)
|
||||||
|
{
|
||||||
|
valueOrDefault = hit.GetValueOrDefault();
|
||||||
|
location = valueOrDefault.Value.Location;
|
||||||
|
}
|
||||||
|
var aeoh = new AutoEngineerOrdersHelper(persistence: new AutoEngineerPersistence(selectedLoco.KeyValueObject), locomotive: selectedLoco);
|
||||||
|
var mw = (location: (Location)location, carId: valueOrDefault?.CarInfo?.car?.id ?? string.Empty);
|
||||||
|
|
||||||
|
aeoh.SetWaypoint(mw.location, mw.carId);
|
||||||
|
aeoh.SetOrdersValue(maybeWaypoint: mw);
|
||||||
|
AutoEngineerDestinationPicker.Shared.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Ray RayForViewportNormalizedPoint(MapWindow i, Vector2 v2) =>
|
||||||
|
i.mapBuilder.mapCamera.ViewportPointToRay(new Vector3(v2.x, v2.y, 0f));
|
||||||
|
|
||||||
|
private static Hit? HitLocation(Location? location, BaseLocomotive selectedLoco)
|
||||||
|
{
|
||||||
|
var _graph = TrainController.Shared.graph;
|
||||||
|
if (location.HasValue)
|
||||||
|
{
|
||||||
|
Location valueOrDefault = location.GetValueOrDefault();
|
||||||
|
TrainController shared = TrainController.Shared;
|
||||||
|
Vector3 position = _graph.GetPosition(valueOrDefault);
|
||||||
|
float num = 2f;
|
||||||
|
Hit? result = null;
|
||||||
|
HashSet<Car> value;
|
||||||
|
using (CollectionPool<HashSet<Car>, Car>.Get(out value))
|
||||||
|
{
|
||||||
|
shared.CheckForCarsAtPoint(position, 2f, value, valueOrDefault);
|
||||||
|
foreach (Car item in value)
|
||||||
|
{
|
||||||
|
if (selectedLoco.EnumerateCoupled().ToHashSet().Contains(item))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!item[item.EndToLogical(Car.End.F)].IsCoupled)
|
||||||
|
{
|
||||||
|
Location location2 = _graph.LocationByMoving(item.LocationF, 0.5f, checkSwitchAgainstMovement: false, stopAtEndOfTrack: true);
|
||||||
|
float distanceBetweenClose = _graph.GetDistanceBetweenClose(valueOrDefault, location2);
|
||||||
|
if (distanceBetweenClose < num)
|
||||||
|
{
|
||||||
|
num = distanceBetweenClose;
|
||||||
|
result = new Hit(location2, (item, Car.End.F));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!item[item.EndToLogical(Car.End.R)].IsCoupled)
|
||||||
|
{
|
||||||
|
Location location3 = _graph.LocationByMoving(item.LocationR, -0.5f, checkSwitchAgainstMovement: false, stopAtEndOfTrack: true).Flipped();
|
||||||
|
float distanceBetweenClose2 = _graph.GetDistanceBetweenClose(valueOrDefault, location3);
|
||||||
|
if (distanceBetweenClose2 < num)
|
||||||
|
{
|
||||||
|
num = distanceBetweenClose2;
|
||||||
|
result = new Hit(location3, (item, Car.End.R));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value.Count > 0)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Hit(valueOrDefault, null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
34
TweaksAndThings/Patches/NoticeExtensions_PostNotice_Patch.cs
Normal file
34
TweaksAndThings/Patches/NoticeExtensions_PostNotice_Patch.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Game.Notices;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Model;
|
||||||
|
using Serilog;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(NoticeExtensions))]
|
||||||
|
[HarmonyPatch(nameof(NoticeExtensions.PostNotice), typeof(Car), typeof(string), typeof(string))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class NoticeExtensions_PostNotice_Patch
|
||||||
|
{
|
||||||
|
private static ILogger _log => Log.ForContext<NoticeExtensions_PostNotice_Patch>();
|
||||||
|
static void Postfix(Car car, string key, string content)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
//Log.Information($"{car.DisplayName} patch PostNotice");
|
||||||
|
if (!string.IsNullOrEmpty(content) &&
|
||||||
|
key.Equals("ai-wpt") &&
|
||||||
|
content.ToLower().Contains("Arrived at Waypoint".ToLower())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
car.PostNotice("ai-wpt-rmroc451", null);
|
||||||
|
}
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.ForContext("car", car).Error(ex, "woops");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -85,13 +85,11 @@ internal class OpsController_AnnounceCoalescedPayments_Patch
|
|||||||
float rate2 = 24 * 2 * 8;// carLoadRate = 8 crew hours in 30 min loading; (24h * 2 to get half hour chunks * 8 hours to load in those chunks)
|
float rate2 = 24 * 2 * 8;// carLoadRate = 8 crew hours in 30 min loading; (24h * 2 to get half hour chunks * 8 hours to load in those chunks)
|
||||||
float num2 = 99999999; //QuantityInStorage for crew-hours (infinite where crew can be shuffling about)
|
float num2 = 99999999; //QuantityInStorage for crew-hours (infinite where crew can be shuffling about)
|
||||||
float quantityToLoad = Mathf.Min(num2, IndustryComponent.RateToValue(rate2, deltaTime));
|
float quantityToLoad = Mathf.Min(num2, IndustryComponent.RateToValue(rate2, deltaTime));
|
||||||
OpsCarAdapter? oca = car.IsCaboose() ? new OpsCarAdapter(car, OpsController.Shared) : null;
|
|
||||||
bool isFull = !car.IsCaboose() ? true : (oca?.IsFull(CrewHoursLoad()) ?? true);
|
|
||||||
if (car.IsCaboose() && !CrewCarStatus(car).spotted)
|
if (car.IsCaboose() && !CrewCarStatus(car).spotted)
|
||||||
{
|
{
|
||||||
CrewCarDict[car.id] = (true, !isFull);
|
CrewCarDict[car.id] = (true, CrewCarDict[car.id].filling);
|
||||||
}
|
}
|
||||||
if (car.IsCabooseAndStoppedForLoadRefresh(isFull))
|
if (car.IsCabooseAndStoppedForLoadRefresh(new OpsCarAdapter(car, OpsController.Shared).IsFull(CrewHoursLoad())))
|
||||||
{
|
{
|
||||||
if (!CrewCarDict[car.id].filling) Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Topping off caboose crew.\"");
|
if (!CrewCarDict[car.id].filling) Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Topping off caboose crew.\"");
|
||||||
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, true);
|
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, true);
|
||||||
@@ -101,15 +99,14 @@ internal class OpsController_AnnounceCoalescedPayments_Patch
|
|||||||
Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Caboose crew topped off.\"");
|
Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Caboose crew topped off.\"");
|
||||||
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, false);
|
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, false);
|
||||||
}
|
}
|
||||||
(oca ?? new OpsCarAdapter(car, OpsController.Shared)).Load(CrewHoursLoad(), quantityToLoad);
|
new OpsCarAdapter(car, OpsController.Shared).Load(CrewHoursLoad(), quantityToLoad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Prefix(IndustryComponent __instance)
|
public static bool Prefix(IndustryComponent __instance)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!StateManager.IsHost || !tweaksAndThings.IsEnabled || !(tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false)) return true;
|
if (!StateManager.IsHost || !tweaksAndThings.IsEnabled() || !tweaksAndThings.EndGearHelpersRequirePayment() || tweaksAndThings.DayLoadCrewHours()) return true;
|
||||||
|
|
||||||
|
|
||||||
TrainController tc = UnityEngine.Object.FindAnyObjectByType<TrainController>();
|
TrainController tc = UnityEngine.Object.FindAnyObjectByType<TrainController>();
|
||||||
try {
|
try {
|
||||||
|
|||||||
169
TweaksAndThings/Patches/RepairTrack_DailyPayables_Patch.cs
Normal file
169
TweaksAndThings/Patches/RepairTrack_DailyPayables_Patch.cs
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
using Game;
|
||||||
|
using Game.State;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Model;
|
||||||
|
using Model.Ops;
|
||||||
|
using Network;
|
||||||
|
using Railloader;
|
||||||
|
using Serilog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using TMPro;
|
||||||
|
using UI.Builder;
|
||||||
|
using UnityEngine;
|
||||||
|
using static Model.Ops.RepairTrack;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(RepairTrack))]
|
||||||
|
[HarmonyPatch(nameof(RepairTrack.DailyPayables), typeof(GameDateTime), typeof(IIndustryContext))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class RepairTrack_DailyPayables_Patch
|
||||||
|
{
|
||||||
|
private static Serilog.ILogger _log => Log.ForContext<RepairTrack_DailyPayables_Patch>();
|
||||||
|
private static Serilog.ILogger ContextualLogger(RepairTrack __instance) =>
|
||||||
|
_log.ForContext("RepairTrackIdentifier", __instance?.Identifier ?? "???")
|
||||||
|
.ForContext("RepairTrackName", __instance?.name ?? "???")
|
||||||
|
.ForContext("RepairTrackDisplayName", __instance?.DisplayName ?? "???");
|
||||||
|
|
||||||
|
public static bool Prefix(RepairTrack __instance, GameDateTime now, IIndustryContext ctx)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!tweaksAndThings.IsEnabled() || !tweaksAndThings.ServiceFundPenalties()) return true;
|
||||||
|
|
||||||
|
|
||||||
|
RepairRateState rateState = __instance.RateState;
|
||||||
|
StateManager shared = StateManager.Shared;
|
||||||
|
|
||||||
|
if (rateState.PayDue < 1E-06f)
|
||||||
|
{
|
||||||
|
rateState.PayDue = 0f;
|
||||||
|
rateState.PaidCurrent = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int num = Mathf.CeilToInt(rateState.PayDue);
|
||||||
|
rateState.PaidCurrent = shared.CanAfford(num);
|
||||||
|
int penalty = Mathf.CeilToInt(!rateState.PaidCurrent ? num * 0.2f : 0);
|
||||||
|
Multiplayer.Broadcast($"{Hyperlink.To(__instance.Industry)}: Paid {num:C0} wages for shop crew${(penalty > 0 ? $"; Overdraft fee of {penalty:C0}" : ".")}");
|
||||||
|
|
||||||
|
__instance.Industry.ApplyToBalance(-num, Ledger.Category.WagesRepair, null, 0, quiet: true);
|
||||||
|
if (penalty > 0)
|
||||||
|
StateManager.Shared.ApplyToBalance(-penalty, Ledger.Category.WagesRepair, null, $"Overdraft: {__instance.Industry.name}", 0, quiet: true);
|
||||||
|
rateState.PayDue = 0f;
|
||||||
|
rateState.PaidCurrent = true;
|
||||||
|
}
|
||||||
|
__instance.RateState = rateState;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(RepairTrack))]
|
||||||
|
[HarmonyPatch(nameof(RepairTrack.EnumerateCarsActual), typeof(IIndustryContext))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class RepairTrack_NeedsRepair_Patch
|
||||||
|
{
|
||||||
|
private static Serilog.ILogger _log => Log.ForContext<RepairTrack_NeedsRepair_Patch>();
|
||||||
|
public static void Postfix(RepairTrack __instance, ref IEnumerable<Car> __result)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!tweaksAndThings.IsEnabled()) return;
|
||||||
|
|
||||||
|
__result = EnumerateCarsActualPatched(__result);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IEnumerable<Car> EnumerateCarsActualPatched(IEnumerable<Car> __result, bool usePatchedVersion = true) =>
|
||||||
|
usePatchedVersion ? __result.Where(NeedsRepairPatched) : __result;
|
||||||
|
|
||||||
|
internal static bool NeedsRepairPatched(Car car)
|
||||||
|
{
|
||||||
|
bool result = TryGetRepairDestination(car, out var overrideTag);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(RepairTrack))]
|
||||||
|
[HarmonyPatch(nameof(RepairTrack.BuildCars), typeof(UIPanelBuilder))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class RepairTrack_BuildCars_Patch
|
||||||
|
{
|
||||||
|
public static bool Prefix(RepairTrack __instance, UIPanelBuilder builder)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!tweaksAndThings.IsEnabled()) return true;
|
||||||
|
|
||||||
|
BuildCarsPatched(__instance, builder);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Car> EnumerateCarsActualOrig(RepairTrack __instance, IIndustryContext ctx)
|
||||||
|
{
|
||||||
|
TrainController trainController = TrainController.Shared;
|
||||||
|
return from car in __instance.EnumerateCars(ctx)
|
||||||
|
select trainController.CarForId(car.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void BuildCarsPatched(RepairTrack __instance, UIPanelBuilder builder)
|
||||||
|
{
|
||||||
|
IndustryContext industryContext = __instance.CreateContext(TimeWeather.Now, 0f);
|
||||||
|
IOrderedEnumerable<IGrouping<RepairGroup, Car>> carGroups = from @group in EnumerateCarsActualOrig(__instance, industryContext).GroupBy(delegate (Car car)
|
||||||
|
{
|
||||||
|
if (InForOverhaul(car))
|
||||||
|
{
|
||||||
|
return RepairGroup.Overhaul;
|
||||||
|
}
|
||||||
|
return RepairTrack_NeedsRepair_Patch.NeedsRepairPatched(car) ? RepairGroup.NeedsRepair : RepairGroup.None;
|
||||||
|
})
|
||||||
|
orderby @group.Key
|
||||||
|
select @group;
|
||||||
|
builder.VStack(delegate (UIPanelBuilder uIPanelBuilder)
|
||||||
|
{
|
||||||
|
float repairRateMultiplier = __instance.RateState.PayRateMultiplier;
|
||||||
|
foreach (IGrouping<RepairGroup, Car> item in carGroups)
|
||||||
|
{
|
||||||
|
RepairGroup repairGroup = item.Key;
|
||||||
|
uIPanelBuilder.AddSection(repairGroup switch
|
||||||
|
{
|
||||||
|
RepairGroup.Overhaul => "Overhauling",
|
||||||
|
RepairGroup.NeedsRepair => "Repairing",
|
||||||
|
RepairGroup.None => "No Work Order Assigned",
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
});
|
||||||
|
foreach (Car car in item.OrderBy((Car car2) => car2.SortName))
|
||||||
|
{
|
||||||
|
uIPanelBuilder.HStack(delegate (UIPanelBuilder uIPanelBuilder2)
|
||||||
|
{
|
||||||
|
uIPanelBuilder2.AddLabel(Hyperlink.To(car)).Width(130f);
|
||||||
|
uIPanelBuilder2.AddLabel($"{Mathf.RoundToInt(car.Condition * 100f)}%").Width(60f).HorizontalTextAlignment(HorizontalAlignmentOptions.Right);
|
||||||
|
string text = "";
|
||||||
|
string title = "";
|
||||||
|
if (repairGroup == RepairGroup.Overhaul)
|
||||||
|
{
|
||||||
|
float overhaulProgress = car.OverhaulProgress;
|
||||||
|
if (overhaulProgress > 0f)
|
||||||
|
{
|
||||||
|
text = $"{(int)(overhaulProgress * 100f)}%";
|
||||||
|
title = "Overhaul Progress";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uIPanelBuilder2.AddLabel(text).Width(60f).HorizontalTextAlignment(HorizontalAlignmentOptions.Right)
|
||||||
|
.Tooltip(title, null);
|
||||||
|
if (repairGroup != RepairGroup.None)
|
||||||
|
{
|
||||||
|
float num = CalculateRepairWorkOverall(car);
|
||||||
|
float num2 = num * 12000f * 0.0005f;
|
||||||
|
string text2 = ((!(repairRateMultiplier > 0f)) ? "Never" : GameDateTimeInterval.DeltaStringMinutes((int)(num / repairRateMultiplier * 60f * 24f), GameDateTimeInterval.Style.Short));
|
||||||
|
uIPanelBuilder2.AddLabel(text2).Width(80f).HorizontalTextAlignment(HorizontalAlignmentOptions.Right)
|
||||||
|
.Tooltip("Time Remaining", "D:HH:MM or H:MM");
|
||||||
|
uIPanelBuilder2.AddLabel($"{num2:F1}T").Width(80f).HorizontalTextAlignment(HorizontalAlignmentOptions.Right)
|
||||||
|
.Tooltip("Repair Parts Needed", null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).Padding(new RectOffset(20, 0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,11 @@
|
|||||||
using Game.State;
|
using Game.State;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using KeyValue.Runtime;
|
using KeyValue.Runtime;
|
||||||
|
using Model.Ops;
|
||||||
using Network;
|
using Network;
|
||||||
using Railloader;
|
using Railloader;
|
||||||
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Patches;
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
@@ -18,8 +21,28 @@ internal class StateManager_OnDayDidChange_Patch
|
|||||||
private static void Postfix(StateManager __instance)
|
private static void Postfix(StateManager __instance)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled || !(tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false)) return;
|
if (!tweaksAndThings.IsEnabled()) return;
|
||||||
if (StateManager.IsHost) PayAutoBrakeCrewWages(__instance);
|
|
||||||
|
if (StateManager.IsHost) DoNewDayActivites(tweaksAndThings, __instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DoNewDayActivites(TweaksAndThingsPlugin tweaksAndThings, StateManager __instance)
|
||||||
|
{
|
||||||
|
if (tweaksAndThings.EndGearHelpersRequirePayment() && tweaksAndThings.DayLoadCrewHours()) LoadCabeese(__instance);
|
||||||
|
if (tweaksAndThings.EndGearHelpersRequirePayment()) PayAutoBrakeCrewWages(__instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadCabeese(StateManager __instance)
|
||||||
|
{
|
||||||
|
foreach (var car in TrainController.Shared.Cars.Where(Car_Extensions.IsCaboose))
|
||||||
|
{
|
||||||
|
var data = car.QuantityCapacityOfLoad(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PayAutoBrakeCrewWages(StateManager __instance)
|
private static void PayAutoBrakeCrewWages(StateManager __instance)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Model;
|
|||||||
using Model.Ops;
|
using Model.Ops;
|
||||||
using Railloader;
|
using Railloader;
|
||||||
using RMROC451.TweaksAndThings.Extensions;
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
using Serilog;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UI.Tags;
|
using UI.Tags;
|
||||||
@@ -23,28 +23,34 @@ internal class TagController_UpdateTag_Patch
|
|||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
|
||||||
if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
|
if (!tweaksAndThings.IsEnabled() || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ProceedWithPostFix(car, tagCallout, tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter());
|
ProceedWithPostFix(car, tagCallout, tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || !tweaksAndThings.CabooseRequiredForLocoOilIndicator());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProceedWithPostFix(Car car, TagCallout tagCallout, bool cabooseRequired)
|
private static void ProceedWithPostFix(Car car, TagCallout tagCallout, bool cabooseRequired)
|
||||||
{
|
{
|
||||||
tagCallout.callout.Title = string.Format(tagTitleFormat, "{0}", car.DisplayName);
|
tagCallout.callout.Title = string.Format(tagTitleFormat, "{0}", Hyperlink.To(car));
|
||||||
List<string> tags = new();
|
List<string> tags = new();
|
||||||
|
string oilSpriteName = string.Empty;// "OilCan";
|
||||||
|
|
||||||
if (OpsController_AnnounceCoalescedPayments_Patch.CrewCarStatus(car).spotted) tags.Add("+");
|
if (OpsController_AnnounceCoalescedPayments_Patch.CrewCarStatus(car).spotted) tags.Add("+");
|
||||||
//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 : $"<cspace=-1em>{TextSprites.Warning}{car.Oiled.TriColorPiePercent(1)}</cspace>");
|
||||||
if (car.EnableOiling) tags.Add(car.HasHotbox ? TextSprites.Hotbox : car.Oiled.TriColorPiePercent(1));
|
if (car.EnableOiling) tags.Add(car.HasHotbox ? TextSprites.Hotbox : car.Oiled.TriColorPiePercent(1, oilSpriteName));
|
||||||
IEnumerable<Car> consist = car.EnumerateCoupled().Where(c => c.EnableOiling);
|
IEnumerable<Car> consist = car.EnumerateCoupled().Where(c => c.EnableOiling);
|
||||||
bool cabooseRequirementFulfilled = consist.CabooseInConsist() || !cabooseRequired || consist.ConsistNoFreight();
|
Func<bool> cabooseRequirementFulfilled = () => (!cabooseRequired || consist.ConsistNoFreight() || car.FindMyCaboose(0.0f, false));
|
||||||
if (cabooseRequirementFulfilled && car.IsLocomotive && !car.NeedsOiling && StateManager.Shared.Storage.OilFeature && consist.Any(c => c.NeedsOiling))
|
if (StateManager.Shared.Storage.OilFeature
|
||||||
tags.Add(consist.OrderBy(c => c.Oiled).FirstOrDefault().Oiled.TriColorPiePercent(1));
|
&& car.IsLocomotive
|
||||||
|
&& !car.NeedsOiling
|
||||||
|
&& (consist.Any(c => c.NeedsOiling) || consist.Any(c => c.HasHotbox)
|
||||||
|
&& cabooseRequirementFulfilled())
|
||||||
|
)
|
||||||
|
tags.Add(consist.Any(c => c.HasHotbox) ? TextSprites.Hotbox : consist.OrderBy(c => c.Oiled).FirstOrDefault().Oiled.TriColorPiePercent(1, oilSpriteName));
|
||||||
if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
|
if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
|
||||||
if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
|
if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ internal class WedgeImage_OnPopulateMesh_Patch
|
|||||||
private static void Postfix(VertexHelper vh)
|
private static void Postfix(VertexHelper vh)
|
||||||
{
|
{
|
||||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return;
|
if (!tweaksAndThings.IsEnabled()) return;
|
||||||
|
|
||||||
vh.Clear(); //clear the image backgrounds for now.
|
vh.Clear(); //clear the image backgrounds for now.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@
|
|||||||
<GameAssembly Include="UnityEngine.InputLegacyModule" />
|
<GameAssembly Include="UnityEngine.InputLegacyModule" />
|
||||||
<GameAssembly Include="UnityEngine.InputModule" />
|
<GameAssembly Include="UnityEngine.InputModule" />
|
||||||
<GameAssembly Include="Unity.TextMeshPro" />
|
<GameAssembly Include="Unity.TextMeshPro" />
|
||||||
|
<GameAssembly Include="Map.Runtime" />
|
||||||
|
<GameAssembly Include="UnityEngine.PhysicsModule" />
|
||||||
|
<GameAssembly Include="UnityEngine.ImageConversionModule" />
|
||||||
|
<GameAssembly Include="UnityEngine.IMGUIModule" />
|
||||||
|
|
||||||
<GameAssembly Include="System.Net.Http" />
|
<GameAssembly Include="System.Net.Http" />
|
||||||
<GameAssembly Include="Core" />
|
<GameAssembly Include="Core" />
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using RMROC451.TweaksAndThings.Enums;
|
|||||||
using UI.Builder;
|
using UI.Builder;
|
||||||
using Model;
|
using Model;
|
||||||
using RMROC451.TweaksAndThings.Extensions;
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings;
|
namespace RMROC451.TweaksAndThings;
|
||||||
|
|
||||||
@@ -23,7 +24,12 @@ public class Settings
|
|||||||
RosterFuelColumnSettings engineRosterFuelColumnSettings,
|
RosterFuelColumnSettings engineRosterFuelColumnSettings,
|
||||||
bool endGearHelpersRequirePayment,
|
bool endGearHelpersRequirePayment,
|
||||||
bool requireConsistCabooseForOilerAndHotboxSpotter,
|
bool requireConsistCabooseForOilerAndHotboxSpotter,
|
||||||
bool cabooseAllowsConsistInfo
|
bool cabooseAllowsConsistInfo,
|
||||||
|
bool cabooseRequiredForLocoTagOilIndication,
|
||||||
|
bool servicingFundPenalty,
|
||||||
|
bool safetyFirst,
|
||||||
|
CrewHourLoadMethod loadCrewHoursMethod,
|
||||||
|
float cabeeseSearchRadiusFtInMeters
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
WebhookSettingsList = webhookSettingsList;
|
WebhookSettingsList = webhookSettingsList;
|
||||||
@@ -32,6 +38,11 @@ public class Settings
|
|||||||
EndGearHelpersRequirePayment = endGearHelpersRequirePayment;
|
EndGearHelpersRequirePayment = endGearHelpersRequirePayment;
|
||||||
RequireConsistCabooseForOilerAndHotboxSpotter = requireConsistCabooseForOilerAndHotboxSpotter;
|
RequireConsistCabooseForOilerAndHotboxSpotter = requireConsistCabooseForOilerAndHotboxSpotter;
|
||||||
CabooseAllowsConsistInfo = cabooseAllowsConsistInfo;
|
CabooseAllowsConsistInfo = cabooseAllowsConsistInfo;
|
||||||
|
CabooseRequiredForLocoTagOilIndication = cabooseRequiredForLocoTagOilIndication;
|
||||||
|
ServicingFundPenalty = servicingFundPenalty;
|
||||||
|
SafetyFirst = safetyFirst;
|
||||||
|
LoadCrewHoursMethod = loadCrewHoursMethod;
|
||||||
|
CabeeseSearchRadiusFtInMeters = cabeeseSearchRadiusFtInMeters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly UIState<string> _selectedTabState = new UIState<string>(null);
|
public readonly UIState<string> _selectedTabState = new UIState<string>(null);
|
||||||
@@ -41,6 +52,11 @@ public class Settings
|
|||||||
public bool EndGearHelpersRequirePayment;
|
public bool EndGearHelpersRequirePayment;
|
||||||
public bool RequireConsistCabooseForOilerAndHotboxSpotter;
|
public bool RequireConsistCabooseForOilerAndHotboxSpotter;
|
||||||
public bool CabooseAllowsConsistInfo;
|
public bool CabooseAllowsConsistInfo;
|
||||||
|
public bool CabooseRequiredForLocoTagOilIndication;
|
||||||
|
public bool ServicingFundPenalty;
|
||||||
|
public bool SafetyFirst;
|
||||||
|
public CrewHourLoadMethod LoadCrewHoursMethod;
|
||||||
|
public float CabeeseSearchRadiusFtInMeters;
|
||||||
|
|
||||||
internal void AddAnotherRow()
|
internal void AddAnotherRow()
|
||||||
{
|
{
|
||||||
@@ -101,6 +117,8 @@ public static class SettingsExtensions
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsEnabled(this TweaksAndThingsPlugin input) =>
|
||||||
|
input?.IsEnabled ?? false;
|
||||||
public static bool CabooseAllowsConsistInfo(this TweaksAndThingsPlugin input) =>
|
public static bool CabooseAllowsConsistInfo(this TweaksAndThingsPlugin input) =>
|
||||||
input?.settings?.CabooseAllowsConsistInfo ?? false;
|
input?.settings?.CabooseAllowsConsistInfo ?? false;
|
||||||
public static bool EndGearHelpersRequirePayment(this TweaksAndThingsPlugin input) =>
|
public static bool EndGearHelpersRequirePayment(this TweaksAndThingsPlugin input) =>
|
||||||
@@ -108,6 +126,16 @@ public static class SettingsExtensions
|
|||||||
public static bool RequireConsistCabooseForOilerAndHotboxSpotter(this TweaksAndThingsPlugin input) =>
|
public static bool RequireConsistCabooseForOilerAndHotboxSpotter(this TweaksAndThingsPlugin input) =>
|
||||||
input?.settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false;
|
input?.settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false;
|
||||||
public static bool CabooseNonMotiveAllowedSetting(this TweaksAndThingsPlugin input, Car car) =>
|
public static bool CabooseNonMotiveAllowedSetting(this TweaksAndThingsPlugin input, Car car) =>
|
||||||
input.EndGearHelpersRequirePayment() && car.set.Cars.CabooseInConsist() && car.NotMotivePower();
|
input.EndGearHelpersRequirePayment() && !car.MotivePower() && (bool)car.FindMyCaboose(0.0f, false);
|
||||||
|
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 DayLoadCrewHours(this TweaksAndThingsPlugin input) =>
|
||||||
|
(input?.settings?.LoadCrewHoursMethod ?? CrewHourLoadMethod.Tracks) == CrewHourLoadMethod.Daily;
|
||||||
|
public static float CabeeseSearchRadiusInMeters(this TweaksAndThingsPlugin input) =>
|
||||||
|
input?.settings?.CabeeseSearchRadiusFtInMeters ?? 21f;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
// Ignore Spelling: RMROC
|
// Ignore Spelling: RMROC
|
||||||
|
|
||||||
using GalaSoft.MvvmLight.Messaging;
|
using GalaSoft.MvvmLight.Messaging;
|
||||||
|
using Game.Messages;
|
||||||
using Game.State;
|
using Game.State;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Railloader;
|
using Railloader;
|
||||||
|
using RMROC451.TweaksAndThings.Commands;
|
||||||
|
using RMROC451.TweaksAndThings.Enums;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using UI.Builder;
|
using UI.Builder;
|
||||||
|
using UnityEngine;
|
||||||
using RMROC451.TweaksAndThings.Enums;
|
using ILogger = Serilog.ILogger;
|
||||||
using RMROC451.TweaksAndThings.Commands;
|
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings;
|
namespace RMROC451.TweaksAndThings;
|
||||||
|
|
||||||
@@ -33,6 +35,8 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
|||||||
IModdingContext moddingContext { get; set; }
|
IModdingContext moddingContext { get; set; }
|
||||||
IModDefinition modDefinition { get; set; }
|
IModDefinition modDefinition { get; set; }
|
||||||
|
|
||||||
|
public string ModDirectory => modDefinition.Directory;
|
||||||
|
|
||||||
static TweaksAndThingsPlugin()
|
static TweaksAndThingsPlugin()
|
||||||
{
|
{
|
||||||
Log.Information("Hello! Static Constructor was called!");
|
Log.Information("Hello! Static Constructor was called!");
|
||||||
@@ -48,7 +52,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
|||||||
|
|
||||||
moddingContext.RegisterConsoleCommand(new EchoCommand());
|
moddingContext.RegisterConsoleCommand(new EchoCommand());
|
||||||
|
|
||||||
settings = moddingContext.LoadSettingsData<Settings>(self.Id);
|
settings = moddingContext.LoadSettingsData<Settings>(self.Id) ?? new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnEnable()
|
public override void OnEnable()
|
||||||
@@ -82,6 +86,10 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
|||||||
settings.WebhookSettingsList =
|
settings.WebhookSettingsList =
|
||||||
settings?.WebhookSettingsList.SanitizeEmptySettings();
|
settings?.WebhookSettingsList.SanitizeEmptySettings();
|
||||||
|
|
||||||
|
builder.AddSection("Adjustments To Base Game", (UIPanelBuilder builder) => {
|
||||||
|
builder.AddLabel("Repair tracks now require cars to be waybilled, or they will not be serviced/overhauled.\nThey will report on the company window's location section as 'No Work Order Assigned'.");
|
||||||
|
});
|
||||||
|
builder.Spacer(spacing * spacing);
|
||||||
builder.AddTabbedPanels(settings._selectedTabState, delegate (UITabbedPanelBuilder tabBuilder)
|
builder.AddTabbedPanels(settings._selectedTabState, delegate (UITabbedPanelBuilder tabBuilder)
|
||||||
{
|
{
|
||||||
tabBuilder.AddTab("Caboose Mods", "cabooseUpdates", CabooseMods);
|
tabBuilder.AddTab("Caboose Mods", "cabooseUpdates", CabooseMods);
|
||||||
@@ -91,21 +99,37 @@ public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static string cabooseUse => "Caboose Use";
|
private static string cabooseUse => "Caboose Use";
|
||||||
private static string autoAiRequirment => "AutoAI\nRequirement";
|
private static string autoAiRequirment => "AutoAI Requirement";
|
||||||
|
private static string locoConsistOilIndication => "Consist Oil Indication";
|
||||||
|
private static float spacing => 2f;
|
||||||
|
|
||||||
private void CabooseMods(UIPanelBuilder builder)
|
private void CabooseMods(UIPanelBuilder builder)
|
||||||
{
|
{
|
||||||
builder.AddField(
|
//UI.GameInput
|
||||||
|
//builder.AddField("Meow",
|
||||||
|
// builder.AddInputBindingControl(
|
||||||
|
|
||||||
|
// )
|
||||||
|
//)
|
||||||
|
|
||||||
|
// InputAction a = new InputAction("connectCarsAndGladhands", InputActionType.Button)
|
||||||
|
|
||||||
|
//var connectCarsAndGladhands = new InputAction("connectCarsAndGladhands");
|
||||||
|
//connectCarsAndGladhands.AddCompositeBinding("connectCarsAndGladhandsComposite")
|
||||||
|
// .With("modifier", "<Keyboard>/leftCtrl")
|
||||||
|
// .With("modifier", "<Keyboard>/leftAlt");
|
||||||
|
|
||||||
|
//builder.AddField("meow", builder.AddInputBindingControl(connectCarsAndGladhands, conflict: true, ()=>))
|
||||||
|
#region EndGearHelperCost
|
||||||
|
builder.AddFieldToggle(
|
||||||
cabooseUse,
|
cabooseUse,
|
||||||
builder.AddToggle(
|
() => this.EndGearHelpersRequirePayment(),
|
||||||
() => settings?.EndGearHelpersRequirePayment ?? false,
|
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new();
|
if (settings == null) settings = new();
|
||||||
settings.EndGearHelpersRequirePayment = enabled;
|
settings.EndGearHelpersRequirePayment = enabled;
|
||||||
builder.Rebuild();
|
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.
|
).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.
|
1.5x multiplier penalty to AI Brake Crew cost if no sufficiently crewed caboose nearby.
|
||||||
@@ -117,26 +141,94 @@ AutoOiler Update: Increases limit that crew will oiling a car from 75% -> 99%, a
|
|||||||
AutoOiler Update: if `{cabooseUse}` & `{autoAiRequirment.Replace("\n", " ")}` checked, then when a caboose is present, the AutoOiler will repair hotboxes afer oiling them to 100%.
|
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)");
|
AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - 30 seconds (Safety Is Everyone's Job)");
|
||||||
|
#endregion
|
||||||
|
|
||||||
builder.AddField(
|
#region CabeeseLoadOptions
|
||||||
|
if (this.EndGearHelpersRequirePayment())
|
||||||
|
{
|
||||||
|
var columns = Enum.GetValues(typeof(CrewHourLoadMethod)).Cast<CrewHourLoadMethod>().Select(i => i.ToString()).ToList();
|
||||||
|
builder.Spacer(spacing);
|
||||||
|
builder.AddField("Refill Option",
|
||||||
|
builder.AddDropdown(
|
||||||
|
columns,
|
||||||
|
(int)(settings?.LoadCrewHoursMethod ?? CrewHourLoadMethod.Tracks),
|
||||||
|
delegate (int column)
|
||||||
|
{
|
||||||
|
if (settings == null) settings = new();
|
||||||
|
settings.LoadCrewHoursMethod = (CrewHourLoadMethod)column;
|
||||||
|
builder.Rebuild();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).Tooltip("Crew Hours Load Option", "Select whether you want to manually reload cabeese via:\n\ntrack method - (team/repair/passenger stop/interchange)\n\ndaily caboose top off - refill to 8h at new day.");
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RequireCabeeseForOiler/HotboxDetection
|
||||||
|
builder.Spacer(spacing);
|
||||||
|
builder.AddFieldToggle(
|
||||||
autoAiRequirment,
|
autoAiRequirment,
|
||||||
builder.AddToggle(
|
() => this.RequireConsistCabooseForOilerAndHotboxSpotter(),
|
||||||
() => settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false,
|
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new();
|
if (settings == null) settings = new();
|
||||||
settings.RequireConsistCabooseForOilerAndHotboxSpotter = enabled;
|
settings.RequireConsistCabooseForOilerAndHotboxSpotter = enabled;
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
}
|
}
|
||||||
|
).Tooltip("AI Hotbox\\Oiler Requires Caboose", $@"A caboose is required in the consist to check for Hotboxes and perform Auto Oiler, if checked.");
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ShowLocomotiveConsistOilIndicator
|
||||||
|
builder.Spacer(spacing);
|
||||||
|
builder.AddFieldToggle(
|
||||||
|
locoConsistOilIndication,
|
||||||
|
() => settings?.CabooseRequiredForLocoTagOilIndication ?? false,
|
||||||
|
delegate (bool enabled)
|
||||||
|
{
|
||||||
|
if (settings == null) settings = new();
|
||||||
|
settings.CabooseRequiredForLocoTagOilIndication = enabled;
|
||||||
|
builder.Rebuild();
|
||||||
|
}
|
||||||
|
).Tooltip(locoConsistOilIndication, $@"A caboose is required in the consist to report the lowest oil level in the consist in the locomotive's tag & roster entry.");
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region SafetyFirst
|
||||||
|
builder.Spacer(spacing);
|
||||||
|
builder.AddFieldToggle(
|
||||||
|
"Safety First!",
|
||||||
|
() => settings?.SafetyFirst ?? false,
|
||||||
|
delegate (bool enabled)
|
||||||
|
{
|
||||||
|
if (settings == null) settings = new();
|
||||||
|
settings.SafetyFirst = enabled;
|
||||||
|
builder.Rebuild();
|
||||||
|
}
|
||||||
|
).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 CabeeseSearchRadius
|
||||||
|
builder.Spacer(spacing);
|
||||||
|
builder.AddField(
|
||||||
|
"Cabeese Search Radius",
|
||||||
|
builder.AddSlider(
|
||||||
|
() => this.CabeeseSearchRadiusInMeters(),
|
||||||
|
() => $"{string.Format(Mathf.CeilToInt(this.CabeeseSearchRadiusInMeters() * 3.28084f).ToString(), "N0")}ft",
|
||||||
|
delegate (float input) {
|
||||||
|
settings = settings ?? new();
|
||||||
|
settings.CabeeseSearchRadiusFtInMeters = Mathf.CeilToInt(input);
|
||||||
|
builder.Rebuild();
|
||||||
|
},
|
||||||
|
minValue: 1f,
|
||||||
|
maxValue: Mathf.CeilToInt(5280f / 2f / 3.28084f),
|
||||||
|
wholeNumbers: true
|
||||||
)
|
)
|
||||||
).Tooltip("AI Engineer Requires Caboose", $@"A caboose is required in the consist to check for Hotboxes and perform Auto Oiler, if checked.");
|
).Tooltip("Cabeese Catchment Area", "How far should the cabeese hunting logic look away from the cars in the area to find a caboose?");
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UiUpdates(UIPanelBuilder builder)
|
private void UiUpdates(UIPanelBuilder builder)
|
||||||
{
|
{
|
||||||
builder.AddField(
|
builder.AddFieldToggle(
|
||||||
"Enable Tag Updates",
|
"Enable Tag Updates",
|
||||||
builder.AddToggle(
|
|
||||||
() => settings?.HandBrakeAndAirTagModifiers ?? false,
|
() => settings?.HandBrakeAndAirTagModifiers ?? false,
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
@@ -144,12 +236,24 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
|||||||
settings.HandBrakeAndAirTagModifiers = enabled;
|
settings.HandBrakeAndAirTagModifiers = enabled;
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
}
|
}
|
||||||
)
|
|
||||||
).Tooltip("Enable Tag Updates", $@"Will suffix tag title with:
|
).Tooltip("Enable Tag Updates", $@"Will suffix tag title with:
|
||||||
{TextSprites.CycleWaybills} if Air System issue.
|
{TextSprites.CycleWaybills} if Air System issue.
|
||||||
{TextSprites.HandbrakeWheel} if there is a handbrake set.
|
{TextSprites.HandbrakeWheel} if there is a handbrake set.
|
||||||
{TextSprites.Hotbox} if a hotbox.");
|
{TextSprites.Hotbox} if a hotbox.");
|
||||||
|
|
||||||
|
builder.Spacer(spacing);
|
||||||
|
builder.AddFieldToggle(
|
||||||
|
"Debt Allowance",
|
||||||
|
() => settings?.ServicingFundPenalty ?? false,
|
||||||
|
delegate (bool enabled)
|
||||||
|
{
|
||||||
|
if (settings == null) settings = new();
|
||||||
|
settings.ServicingFundPenalty = enabled;
|
||||||
|
builder.Rebuild();
|
||||||
|
}
|
||||||
|
).Tooltip("Allow Insufficient Funds", $@"Will allow interchange service and repair shops to still function when you are insolvent, at a 20% overdraft fee.");
|
||||||
|
|
||||||
|
builder.Spacer(spacing);
|
||||||
EngineRosterShowsFuelStatusUISection(builder);
|
EngineRosterShowsFuelStatusUISection(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +262,7 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
|||||||
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)
|
||||||
{
|
{
|
||||||
|
builder.Spacer(spacing);
|
||||||
builder.AddField(
|
builder.AddField(
|
||||||
"Enable",
|
"Enable",
|
||||||
builder.AddDropdown(columns, (int)(settings?.EngineRosterFuelColumnSettings?.EngineRosterFuelStatusColumn ?? EngineRosterFuelDisplayColumn.None),
|
builder.AddDropdown(columns, (int)(settings?.EngineRosterFuelColumnSettings?.EngineRosterFuelStatusColumn ?? EngineRosterFuelDisplayColumn.None),
|
||||||
@@ -170,9 +275,9 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
|||||||
)
|
)
|
||||||
).Tooltip("Enable Fuel Display in Engine Roster", $"Will add reaming fuel indication to Engine Roster (with details in roster row tool tip), Examples : {string.Join(" ", Enumerable.Range(0, 4).Select(i => TextSprites.PiePercent(i, 4)))}");
|
).Tooltip("Enable Fuel Display in Engine Roster", $"Will add reaming fuel indication to Engine Roster (with details in roster row tool tip), Examples : {string.Join(" ", Enumerable.Range(0, 4).Select(i => TextSprites.PiePercent(i, 4)))}");
|
||||||
|
|
||||||
builder.AddField(
|
builder.Spacer(spacing);
|
||||||
|
builder.AddFieldToggle(
|
||||||
"Always Visible?",
|
"Always Visible?",
|
||||||
builder.AddToggle(
|
|
||||||
() => settings?.EngineRosterFuelColumnSettings?.EngineRosterShowsFuelStatusAlways ?? false,
|
() => settings?.EngineRosterFuelColumnSettings?.EngineRosterShowsFuelStatusAlways ?? false,
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
@@ -180,7 +285,6 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
|||||||
settings.EngineRosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways = enabled;
|
settings.EngineRosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways = enabled;
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
}
|
}
|
||||||
)
|
|
||||||
).Tooltip("Fuel Display in Engine Roster Always Visible", $"Always displayed, if you want it hidden and only shown when you care to see, uncheck this, and then you can press ALT for it to populate on the next UI refresh cycle.");
|
).Tooltip("Fuel Display in Engine Roster Always Visible", $"Always displayed, if you want it hidden and only shown when you care to see, uncheck this, and then you can press ALT for it to populate on the next UI refresh cycle.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user