mirror of
https://github.com/rmroc451/TweaksAndThings.git
synced 2025-12-17 01:39:38 -06:00
Compare commits
9 Commits
v0.1.5
...
15-add-loc
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a228be5a8 | |||
| 2ed33465d9 | |||
| 67d39801de | |||
| 8dc87f312d | |||
| 9cb788d86a | |||
| 2e47536028 | |||
| b6edc93636 | |||
| 6693a762c9 | |||
| fc43d54815 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -396,4 +396,3 @@ FodyWeavers.xsd
|
|||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
/TweaksAndThings/Commands/EchoCommand.cs
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''">
|
<PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''">
|
||||||
<MajorVersion Condition="'$(MajorVersion)' == ''">0</MajorVersion>
|
<MajorVersion Condition="'$(MajorVersion)' == ''">0</MajorVersion>
|
||||||
<MinorVersion Condition="'$(MinorVersion)' == ''">1</MinorVersion>
|
<MinorVersion Condition="'$(MinorVersion)' == ''">1</MinorVersion>
|
||||||
<PatchVersion Condition="'$(PatchVersion)' == ''">5</PatchVersion>
|
<PatchVersion Condition="'$(PatchVersion)' == ''">6</PatchVersion>
|
||||||
<AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion>
|
<AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion>
|
||||||
<FileVersion>$(AssemblyVersion)</FileVersion>
|
<FileVersion>$(AssemblyVersion)</FileVersion>
|
||||||
<ProductVersion>$(AssemblyVersion)</ProductVersion>
|
<ProductVersion>$(AssemblyVersion)</ProductVersion>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ VisualStudioVersion = 17.4.33122.133
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{452A23A6-81C8-49C6-A7EE-95FD9377F896}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{452A23A6-81C8-49C6-A7EE-95FD9377F896}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.gitignore = .gitignore
|
||||||
Directory.Build.props = Directory.Build.props
|
Directory.Build.props = Directory.Build.props
|
||||||
Directory.Build.targets = Directory.Build.targets
|
Directory.Build.targets = Directory.Build.targets
|
||||||
Paths.user = Paths.user
|
Paths.user = Paths.user
|
||||||
|
|||||||
44
TweaksAndThings/Commands/CrewUpdateCommand.cs
Normal file
44
TweaksAndThings/Commands/CrewUpdateCommand.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Game.State;
|
||||||
|
using Helpers;
|
||||||
|
using Model.OpsNew;
|
||||||
|
using Network;
|
||||||
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
|
using System.Linq;
|
||||||
|
using UI.Console;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Commands;
|
||||||
|
|
||||||
|
[ConsoleCommand("/cu", "generate a formatted message about a locomotive's status.")]
|
||||||
|
public class EchoCommand : IConsoleCommand
|
||||||
|
{
|
||||||
|
public string Execute(string[] comps)
|
||||||
|
{
|
||||||
|
if (comps.Length < 4)
|
||||||
|
{
|
||||||
|
return "Usage: /cu <car>|. +|- <message>";
|
||||||
|
}
|
||||||
|
|
||||||
|
string query = comps[1];
|
||||||
|
Model.Car car = query == "." ? TrainController.Shared.SelectedLocomotive : TrainController.Shared.CarForString(query);
|
||||||
|
|
||||||
|
if (car.DetermineFuelCar() == null)
|
||||||
|
{
|
||||||
|
return "Car not found.";
|
||||||
|
}
|
||||||
|
string message = string.Join(" ", comps.Skip(3)).Truncate(512);
|
||||||
|
|
||||||
|
switch (comps[2])
|
||||||
|
{
|
||||||
|
case "+":
|
||||||
|
break;
|
||||||
|
case "-":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "+ to include area or - to leave it out";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comps[2] == "+") message += $" {OpsController.Shared.ClosestArea(car)?.name ?? "???"}";
|
||||||
|
Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\"");
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,13 @@
|
|||||||
{
|
{
|
||||||
"id": "railloader",
|
"id": "railloader",
|
||||||
"notBefore": "1.8.1"
|
"notBefore": "1.8.1"
|
||||||
}
|
},
|
||||||
|
"Zamu.StrangeCustoms"
|
||||||
],
|
],
|
||||||
"assemblies": [ "RMROC451.TweaksAndThings" ]
|
"assemblies": [ "RMROC451.TweaksAndThings" ],
|
||||||
|
"mixintos": {
|
||||||
|
"container:ne-caboose01": "file(mroc-cabeese.json)",
|
||||||
|
"container:ne-caboose02": "file(mroc-cabeese.json)",
|
||||||
|
"container:ne-caboose03": "file(mroc-cabeese.json)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,5 +3,6 @@
|
|||||||
public enum MrocHelperType
|
public enum MrocHelperType
|
||||||
{
|
{
|
||||||
Handbrake,
|
Handbrake,
|
||||||
GladhandAndAnglecock
|
GladhandAndAnglecock,
|
||||||
|
BleedAirSystem
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +1,121 @@
|
|||||||
using Model;
|
using Helpers;
|
||||||
|
using Model;
|
||||||
|
using Model.Definition.Data;
|
||||||
|
using Model.OpsNew;
|
||||||
|
using Serilog;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Extensions
|
namespace RMROC451.TweaksAndThings.Extensions;
|
||||||
|
|
||||||
|
public static class Car_Extensions
|
||||||
{
|
{
|
||||||
public static class Car_Extensions
|
public static bool EndAirSystemIssue(this Car car)
|
||||||
{
|
{
|
||||||
public static bool EndAirSystemIssue(this Car car)
|
bool AEndAirSystemIssue = car[Car.LogicalEnd.A].IsCoupled && !car[Car.LogicalEnd.A].IsAirConnectedAndOpen;
|
||||||
|
bool BEndAirSystemIssue = car[Car.LogicalEnd.B].IsCoupled && !car[Car.LogicalEnd.B].IsAirConnectedAndOpen;
|
||||||
|
bool EndAirSystemIssue = AEndAirSystemIssue || BEndAirSystemIssue;
|
||||||
|
return EndAirSystemIssue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HandbrakeApplied(this Model.Car car) =>
|
||||||
|
car.air.handbrakeApplied;
|
||||||
|
|
||||||
|
public static bool CarOrEndGearIssue(this Model.Car car) =>
|
||||||
|
car.EndAirSystemIssue() || car.HandbrakeApplied();
|
||||||
|
|
||||||
|
public static bool CarAndEndGearIssue(this Model.Car car) =>
|
||||||
|
car.EndAirSystemIssue() && car.HandbrakeApplied();
|
||||||
|
|
||||||
|
public static Car? DetermineFuelCar(this Car engine, bool returnEngineIfNull = false)
|
||||||
|
{
|
||||||
|
if (engine == null) return null;
|
||||||
|
Car? car;
|
||||||
|
if (engine is SteamLocomotive steamLocomotive && new Func<Car>(steamLocomotive.FuelCar) != null)
|
||||||
{
|
{
|
||||||
bool AEndAirSystemIssue = car[Car.LogicalEnd.A].IsCoupled && !car[Car.LogicalEnd.A].IsAirConnectedAndOpen;
|
car = steamLocomotive.FuelCar();
|
||||||
bool BEndAirSystemIssue = car[Car.LogicalEnd.B].IsCoupled && !car[Car.LogicalEnd.B].IsAirConnectedAndOpen;
|
|
||||||
bool EndAirSystemIssue = AEndAirSystemIssue || BEndAirSystemIssue;
|
|
||||||
return EndAirSystemIssue;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
public static bool HandbrakeApplied(this Model.Car car) =>
|
|
||||||
car.air.handbrakeApplied;
|
|
||||||
|
|
||||||
public static bool CarOrEndGearIssue(this Model.Car car) =>
|
|
||||||
car.EndAirSystemIssue() || car.HandbrakeApplied();
|
|
||||||
|
|
||||||
public static bool CarAndEndGearIssue(this Model.Car car) =>
|
|
||||||
car.EndAirSystemIssue() && car.HandbrakeApplied();
|
|
||||||
|
|
||||||
public static Car? DetermineFuelCar(this Car engine, bool returnEngineIfNull = false)
|
|
||||||
{
|
{
|
||||||
Car? car;
|
car = engine is DieselLocomotive ? engine : null;
|
||||||
if (engine is SteamLocomotive steamLocomotive && new Func<Car>(steamLocomotive.FuelCar) != null)
|
if (returnEngineIfNull && car == null) car = engine;
|
||||||
{
|
|
||||||
car = steamLocomotive.FuelCar();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
car = engine is DieselLocomotive ? engine : null;
|
|
||||||
if (returnEngineIfNull && car == null) car = engine;
|
|
||||||
}
|
|
||||||
return car;
|
|
||||||
}
|
}
|
||||||
|
return car;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool NotMotivePower(this Car car) => car is not BaseLocomotive && car.Archetype != Model.Definition.CarArchetype.Tender;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For every car in the consist, cost 1 minute of AI Engineer time.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="consist"></param>
|
||||||
|
public static int CalculateCostForAutoEngineerEndGearSetting(this IEnumerable<Car> consist) =>
|
||||||
|
consist.Count() * 60;
|
||||||
|
|
||||||
|
public static bool IsCaboose(this Car car) => car.Archetype == Model.Definition.CarArchetype.Caboose;
|
||||||
|
|
||||||
|
public static bool CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(c => c.IsCaboose());
|
||||||
|
|
||||||
|
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, HashSet<string> carIdsCheckedAlready, bool decrement = false)
|
||||||
|
{
|
||||||
|
Car? output = null;
|
||||||
|
if (carIdsCheckedAlready.Contains(car.id) || !car.IsCaboose()) return null;
|
||||||
|
|
||||||
|
List<LoadSlot> loadSlots = car.Definition.LoadSlots;
|
||||||
|
for (int i = 0; i < loadSlots.Count; i++)
|
||||||
|
{
|
||||||
|
CarLoadInfo? loadInfo = car.GetLoadInfo(i);
|
||||||
|
if (loadInfo.HasValue)
|
||||||
|
{
|
||||||
|
CarLoadInfo valueOrDefault = loadInfo.GetValueOrDefault();
|
||||||
|
output = valueOrDefault.Quantity >= timeNeeded ? car : null;
|
||||||
|
if (decrement && output != null) output.SetLoadInfo(i, valueOrDefault with { Quantity = valueOrDefault.Quantity - timeNeeded });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Car? HuntingForCabeeseNearCar(this Car car, float timeNeeded, TrainController tc, HashSet<string> carIdsCheckedAlready, bool decrement = false)
|
||||||
|
{
|
||||||
|
List<(string carId, float distance)> source =
|
||||||
|
CarsNearCurrentCar(car, timeNeeded, tc, carIdsCheckedAlready, decrement);
|
||||||
|
|
||||||
|
Car output = FindNearestCabooseFromNearbyCars(tc, source);
|
||||||
|
if (output != null) output.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready, decrement);
|
||||||
|
|
||||||
|
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);
|
||||||
|
Rect rect = new Rect(new Vector2(center.x - 30f, center.z - 30f), Vector2.one * 30f * 2f);
|
||||||
|
var cars = tc.CarIdsInRect(rect);
|
||||||
|
Log.Information($"{nameof(HuntingForCabeeseNearCar)} => {cars.Count()}");
|
||||||
|
List<(string carId, float distance)> source =
|
||||||
|
cars
|
||||||
|
.Select(carId =>
|
||||||
|
{
|
||||||
|
Car car = tc.CarForId(carId);
|
||||||
|
if (car == null || !car.CabooseWithSufficientCrewHours(timeNeeded, carIdsCheckedAlready))
|
||||||
|
{
|
||||||
|
return (carId: carId, distance: 1000f);
|
||||||
|
}
|
||||||
|
Vector3 a = WorldTransformer.WorldToGame(car.GetMotionSnapshot().Position);
|
||||||
|
return (carId: carId, distance: Vector3.Distance(a, center));
|
||||||
|
}).ToList();
|
||||||
|
return source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
TweaksAndThings/Extensions/Load_Extensions.cs
Normal file
28
TweaksAndThings/Extensions/Load_Extensions.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Core;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Extensions;
|
||||||
|
|
||||||
|
public static class Load_Extensions
|
||||||
|
{
|
||||||
|
//https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings?redirectedfrom=MSDN#the--section-separator
|
||||||
|
//https://dotnetfiddle.net/iHVevM
|
||||||
|
public static string FormatCrewHours(this float quantity, string description)
|
||||||
|
{
|
||||||
|
var ts = TimeSpan.FromHours(quantity);
|
||||||
|
float minutes = ts.Minutes - (ts.Minutes % 15);
|
||||||
|
|
||||||
|
string output = string.Format("{0:;;No}{1:##}{2:\\.##;\\.##;.} {3} {4}",
|
||||||
|
ts.Hours + minutes,
|
||||||
|
ts.Hours, (minutes / 60.0f) * 100,
|
||||||
|
description,
|
||||||
|
"Hour".Pluralize(quantity == 1 ? 1 : 0)
|
||||||
|
).Trim();
|
||||||
|
|
||||||
|
if (ts.Hours < 1)
|
||||||
|
{
|
||||||
|
output = string.Format("{0} {1} Minutes", ts.Minutes, description).Trim();
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
using Game.State;
|
using Game.State;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using KeyValue.Runtime;
|
using KeyValue.Runtime;
|
||||||
|
using Model;
|
||||||
|
using Model.OpsNew;
|
||||||
|
using Network;
|
||||||
using Railloader;
|
using Railloader;
|
||||||
using RMROC451.TweaksAndThings.Enums;
|
using RMROC451.TweaksAndThings.Enums;
|
||||||
using RMROC451.TweaksAndThings.Extensions;
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
@@ -11,6 +14,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UI.Builder;
|
using UI.Builder;
|
||||||
using UI.CarInspector;
|
using UI.CarInspector;
|
||||||
|
using UI.Tags;
|
||||||
using static Model.Car;
|
using static Model.Car;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings.Patches;
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
@@ -18,7 +22,7 @@ namespace RMROC451.TweaksAndThings.Patches;
|
|||||||
[HarmonyPatch(typeof(CarInspector))]
|
[HarmonyPatch(typeof(CarInspector))]
|
||||||
[HarmonyPatch(nameof(CarInspector.PopulateCarPanel), typeof(UIPanelBuilder))]
|
[HarmonyPatch(nameof(CarInspector.PopulateCarPanel), typeof(UIPanelBuilder))]
|
||||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
public class CarInspector_PopulateCarPanel_Patch
|
internal class CarInspector_PopulateCarPanel_Patch
|
||||||
{
|
{
|
||||||
private static IEnumerable<LogicalEnd> ends = Enum.GetValues(typeof(LogicalEnd)).Cast<LogicalEnd>();
|
private static IEnumerable<LogicalEnd> ends = Enum.GetValues(typeof(LogicalEnd)).Cast<LogicalEnd>();
|
||||||
|
|
||||||
@@ -31,8 +35,9 @@ public class CarInspector_PopulateCarPanel_Patch
|
|||||||
private static bool Prefix(CarInspector __instance, UIPanelBuilder builder)
|
private static bool Prefix(CarInspector __instance, UIPanelBuilder builder)
|
||||||
{
|
{
|
||||||
|
|
||||||
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
if (!tweaksAndThings.IsEnabled) return true;
|
if (!tweaksAndThings.IsEnabled) return true;
|
||||||
|
bool buttonsHaveCost = tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false;
|
||||||
|
|
||||||
var consist = __instance._car.EnumerateCoupled(LogicalEnd.A);
|
var consist = __instance._car.EnumerateCoupled(LogicalEnd.A);
|
||||||
builder = AddCarConsistRebuildObservers(builder, consist);
|
builder = AddCarConsistRebuildObservers(builder, consist);
|
||||||
@@ -40,20 +45,23 @@ public class CarInspector_PopulateCarPanel_Patch
|
|||||||
builder.HStack(delegate (UIPanelBuilder hstack)
|
builder.HStack(delegate (UIPanelBuilder hstack)
|
||||||
{
|
{
|
||||||
var buttonName = $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} {TextSprites.HandbrakeWheel}";
|
var buttonName = $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} {TextSprites.HandbrakeWheel}";
|
||||||
hstack.AddButtonCompact(buttonName, delegate
|
hstack.AddButtonCompact(buttonName, delegate {
|
||||||
{
|
MrocConsistHelper(__instance._car, MrocHelperType.Handbrake, buttonsHaveCost);
|
||||||
MrocConsistHelper(__instance._car, MrocHelperType.Handbrake);
|
|
||||||
hstack.Rebuild();
|
hstack.Rebuild();
|
||||||
}).Tooltip(buttonName, $"Iterates over cars in this consist and {(consist.Any(c => c.HandbrakeApplied()) ? "releases" : "sets")} {TextSprites.HandbrakeWheel}.");
|
}).Tooltip(buttonName, $"Iterates over cars in this consist and {(consist.Any(c => c.HandbrakeApplied()) ? "releases" : "sets")} {TextSprites.HandbrakeWheel}.");
|
||||||
|
|
||||||
if (consist.Any(c => c.EndAirSystemIssue()))
|
if (consist.Any(c => c.EndAirSystemIssue()))
|
||||||
{
|
{
|
||||||
hstack.AddButtonCompact("Connect Air Lines", delegate
|
hstack.AddButtonCompact("Connect Air", delegate {
|
||||||
{
|
MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
|
||||||
MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock);
|
|
||||||
hstack.Rebuild();
|
hstack.Rebuild();
|
||||||
}).Tooltip("Connect Consist Air Lines", "Iterates over each car in this consist and connects gladhands and opens anglecocks.");
|
}).Tooltip("Connect Consist Air", "Iterates over each car in this consist and connects gladhands and opens anglecocks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hstack.AddButtonCompact("Bleed Consist", delegate {
|
||||||
|
MrocConsistHelper(__instance._car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
|
||||||
|
hstack.Rebuild();
|
||||||
|
}).Tooltip("Bleed Air Lines", "Iterates over each car in this consist and bleeds the air out of the lines.");
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -61,28 +69,37 @@ public class CarInspector_PopulateCarPanel_Patch
|
|||||||
|
|
||||||
private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist)
|
private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist)
|
||||||
{
|
{
|
||||||
|
TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>();
|
||||||
foreach (Model.Car car in consist)
|
foreach (Model.Car car in consist)
|
||||||
{
|
{
|
||||||
builder = AddObserver(builder, car, PropertyChange.KeyForControl(PropertyChange.Control.Handbrake));
|
builder = AddObserver(builder, car, PropertyChange.KeyForControl(PropertyChange.Control.Handbrake), tagController);
|
||||||
foreach (LogicalEnd logicalEnd in ends)
|
foreach (LogicalEnd logicalEnd in ends)
|
||||||
{
|
{
|
||||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsCoupled, car.LogicalToEnd(logicalEnd)));
|
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsCoupled, car.LogicalToEnd(logicalEnd)), tagController);
|
||||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsAirConnected, car.LogicalToEnd(logicalEnd)));
|
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsAirConnected, car.LogicalToEnd(logicalEnd)), tagController);
|
||||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.Anglecock, car.LogicalToEnd(logicalEnd)));
|
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.Anglecock, car.LogicalToEnd(logicalEnd)), tagController);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UIPanelBuilder AddObserver(UIPanelBuilder builder, Model.Car car, string key)
|
private static UIPanelBuilder AddObserver(UIPanelBuilder builder, Model.Car car, string key, TagController tagController)
|
||||||
{
|
{
|
||||||
builder.AddObserver(
|
builder.AddObserver(
|
||||||
car.KeyValueObject.Observe(
|
car.KeyValueObject.Observe(
|
||||||
key,
|
key,
|
||||||
delegate (Value value)
|
delegate (Value value)
|
||||||
{
|
{
|
||||||
builder.Rebuild();
|
try
|
||||||
|
{
|
||||||
|
tagController.UpdateTag(car, car.TagCallout, OpsController.Shared);
|
||||||
|
builder.Rebuild();
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
@@ -101,21 +118,24 @@ public class CarInspector_PopulateCarPanel_Patch
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType)
|
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost)
|
||||||
{
|
{
|
||||||
|
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
|
||||||
IEnumerable<Model.Car> consist = car.EnumerateCoupled(LogicalEnd.A);
|
IEnumerable<Model.Car> consist = car.EnumerateCoupled(LogicalEnd.A);
|
||||||
|
//Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
||||||
|
|
||||||
|
CalculateCostIfEnabled(car, mrocHelperType, buttonsHaveCost, consist);
|
||||||
|
|
||||||
Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
|
||||||
switch (mrocHelperType)
|
switch (mrocHelperType)
|
||||||
{
|
{
|
||||||
case MrocHelperType.Handbrake:
|
case MrocHelperType.Handbrake:
|
||||||
if (consist.Any(c => c.HandbrakeApplied()))
|
if (consist.Any(c => c.HandbrakeApplied()))
|
||||||
{
|
{
|
||||||
consist.Do(c => c.SetHandbrake(false));
|
consist.Do(c => c.SetHandbrake(false));
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
|
consist = consist.Where(c => c is not BaseLocomotive && c.Archetype != Model.Definition.CarArchetype.Tender);
|
||||||
consist = consist.Where(c => c.DetermineFuelCar(true) != null);
|
|
||||||
Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
||||||
//when ApplyHandbrakesAsNeeded is called, and the consist contains an engine, it stops applying brakes.
|
//when ApplyHandbrakesAsNeeded is called, and the consist contains an engine, it stops applying brakes.
|
||||||
tc.ApplyHandbrakesAsNeeded(consist.ToList(), PlaceTrainHandbrakes.Automatic);
|
tc.ApplyHandbrakesAsNeeded(consist.ToList(), PlaceTrainHandbrakes.Automatic);
|
||||||
@@ -130,20 +150,72 @@ public class CarInspector_PopulateCarPanel_Patch
|
|||||||
|
|
||||||
StateManager.ApplyLocal(
|
StateManager.ApplyLocal(
|
||||||
new PropertyChange(
|
new PropertyChange(
|
||||||
c.id,
|
c.id,
|
||||||
KeyValueKeyFor(EndGearStateKey.Anglecock, c.LogicalToEnd(end)),
|
KeyValueKeyFor(EndGearStateKey.Anglecock, c.LogicalToEnd(end)),
|
||||||
new FloatPropertyValue(endGear.IsCoupled ? 1f : 0f)
|
new FloatPropertyValue(endGear.IsCoupled ? 1f : 0f)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (c.TryGetAdjacentCar(end, out Model.Car c2))
|
if (c.TryGetAdjacentCar(end, out Model.Car c2)) StateManager.ApplyLocal(new SetGladhandsConnected(c.id, c2.id, true));
|
||||||
{
|
|
||||||
StateManager.ApplyLocal(new SetGladhandsConnected(c.id, c2.id, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MrocHelperType.BleedAirSystem:
|
||||||
|
consist = consist.Where(c => c.NotMotivePower());
|
||||||
|
Log.Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}");
|
||||||
|
foreach (Model.Car bleed in consist)
|
||||||
|
{
|
||||||
|
StateManager.ApplyLocal(new PropertyChange(bleed.id, PropertyChange.Control.Bleed, 1));
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CalculateCostIfEnabled(Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost, IEnumerable<Car> consist)
|
||||||
|
{
|
||||||
|
if (buttonsHaveCost)
|
||||||
|
{
|
||||||
|
float originalTimeCost = consist.CalculateCostForAutoEngineerEndGearSetting();
|
||||||
|
float timeCost = originalTimeCost;
|
||||||
|
float crewCost = timeCost / 3600; //hours of time deducted from caboose.
|
||||||
|
var tsString = crewCost.FormatCrewHours(IndustryComponent_Service_Patch.CrewHoursLoad().description);
|
||||||
|
Car? cabooseWithAvailCrew = NearbyCabooseWithAvailableCrew(car, crewCost, buttonsHaveCost);
|
||||||
|
if (cabooseWithAvailCrew == null) timeCost *= 1.5f;
|
||||||
|
var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose";
|
||||||
|
|
||||||
|
Log.Information($"{nameof(MrocConsistHelper)} {mrocHelperType} : [VACINITY CABEESE FOUND:{cabooseWithAvailCrew?.ToString() ?? "NONE"}] => Consist Length {consist.Count()} => costs {timeCost / 60} minutes of AI Engineer time, $5 per hour = ~${Math.Ceiling((decimal)(timeCost / 3600) * 5)} (*2 if no caboose nearby)");
|
||||||
|
|
||||||
|
|
||||||
|
Multiplayer.SendError(StateManager.Shared._playersManager.LocalPlayer, $"{(cabooseWithAvailCrew != null ? $"{cabooseWithAvailCrew.DisplayName} Hours Adjusted: ({tsString})\n" : string.Empty)}Wages: ~(${Math.Ceiling((decimal)(timeCost / 3600) * 5)})");
|
||||||
|
|
||||||
|
if (buttonsHaveCost) StateManager_OnDayDidChange_Patch.UnbilledAutoBrakeCrewRunDuration += timeCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(LogicalEnd.A);
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ namespace RMROC451.TweaksAndThings.Patches;
|
|||||||
[HarmonyPatch(typeof(EngineRosterRow))]
|
[HarmonyPatch(typeof(EngineRosterRow))]
|
||||||
[HarmonyPatch(nameof(EngineRosterRow.Refresh))]
|
[HarmonyPatch(nameof(EngineRosterRow.Refresh))]
|
||||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
public class EngineRosterRow_Refresh_Patch
|
internal class EngineRosterRow_Refresh_Patch
|
||||||
{
|
{
|
||||||
public static void Postfix(EngineRosterRow __instance)
|
public static void Postfix(EngineRosterRow __instance)
|
||||||
{
|
{
|
||||||
TweaksAndThings? tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
TweaksAndThingsPlugin? tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
RosterFuelColumnSettings? rosterFuelColumnSettings = tweaksAndThings?.settings?.EngineRosterFuelColumnSettings;
|
RosterFuelColumnSettings? rosterFuelColumnSettings = tweaksAndThings?.settings?.EngineRosterFuelColumnSettings;
|
||||||
|
|
||||||
if (tweaksAndThings == null ||
|
if (tweaksAndThings == null ||
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace RMROC451.TweaksAndThings.Patches;
|
|||||||
[HarmonyPatch(typeof(ExpandedConsole))]
|
[HarmonyPatch(typeof(ExpandedConsole))]
|
||||||
[HarmonyPatch(nameof(ExpandedConsole.Add))]
|
[HarmonyPatch(nameof(ExpandedConsole.Add))]
|
||||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
public class ExpandedConsole_Add_Patch
|
internal class ExpandedConsole_Add_Patch
|
||||||
{
|
{
|
||||||
private static void Prefix(ref UI.Console.Console.Entry entry)
|
private static void Prefix(ref UI.Console.Console.Entry entry)
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ public class ExpandedConsole_Add_Patch
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
StateManager shared = StateManager.Shared;
|
StateManager shared = StateManager.Shared;
|
||||||
GameStorage gameStorage = shared.Storage;
|
GameStorage gameStorage = shared.Storage;
|
||||||
WebhookSettings settings = tweaksAndThings?.settings?.WebhookSettingsList?.FirstOrDefault(ws => ws.RailroadMark == gameStorage.RailroadMark);
|
WebhookSettings settings = tweaksAndThings?.settings?.WebhookSettingsList?.FirstOrDefault(ws => ws.RailroadMark == gameStorage.RailroadMark);
|
||||||
|
|||||||
139
TweaksAndThings/Patches/IndustryComponent_Service_Patch.cs
Normal file
139
TweaksAndThings/Patches/IndustryComponent_Service_Patch.cs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
using Game.State;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Model;
|
||||||
|
using Model.Definition.Data;
|
||||||
|
using Model.Ops.Definition;
|
||||||
|
using Model.OpsNew;
|
||||||
|
using Railloader;
|
||||||
|
using RMROC451.TweaksAndThings.Extensions;
|
||||||
|
using Serilog;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(CarExtensions))]
|
||||||
|
[HarmonyPatch(nameof(CarExtensions.LoadString), typeof(CarLoadInfo), typeof(Load))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class CarExtensions_LoadString_Patch
|
||||||
|
{
|
||||||
|
public static bool Prefix(CarLoadInfo info, Load load, ref string __result)
|
||||||
|
{
|
||||||
|
bool output = load.id == IndustryComponent_Service_Patch.CrewHoursLoad().id;
|
||||||
|
if (output) __result = info.Quantity.FormatCrewHours(load.description);
|
||||||
|
|
||||||
|
return !output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(CarPrototypeLibrary))]
|
||||||
|
[HarmonyPatch(nameof(CarPrototypeLibrary.LoadForId), typeof(string))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class CarPrototypeLibrary_LoadForId_Patch
|
||||||
|
{
|
||||||
|
public static bool Prefix(string loadId, ref Load __result)
|
||||||
|
{
|
||||||
|
Load load = IndustryComponent_Service_Patch.CrewHoursLoad();
|
||||||
|
if (loadId == load.id) __result = load;
|
||||||
|
|
||||||
|
return __result == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(TeamTrack))]
|
||||||
|
[HarmonyPatch(nameof(TeamTrack.Service), typeof(IIndustryContext))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class TeamTrack_Service_Patch
|
||||||
|
{
|
||||||
|
public static bool Prefix(IndustryComponent __instance, IIndustryContext ctx)
|
||||||
|
{
|
||||||
|
//Log.Information($"{nameof(SimplePassengerStop_Service_Patch)} => {((IndustryContext)ctx)._industry.name}");
|
||||||
|
return IndustryComponent_Service_Patch.Prefix(__instance, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(RepairTrack))]
|
||||||
|
[HarmonyPatch(nameof(RepairTrack.Service), typeof(IIndustryContext))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class RepairTrack_Service_Patch
|
||||||
|
{
|
||||||
|
public static bool Prefix(IndustryComponent __instance, IIndustryContext ctx)
|
||||||
|
{
|
||||||
|
//Log.Information($"{nameof(SimplePassengerStop_Service_Patch)} => {((IndustryContext)ctx)._industry.name}");
|
||||||
|
return IndustryComponent_Service_Patch.Prefix(__instance, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(SimplePassengerStop))]
|
||||||
|
[HarmonyPatch(nameof(SimplePassengerStop.Service), typeof(IIndustryContext))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class SimplePassengerStop_Service_Patch
|
||||||
|
{
|
||||||
|
public static bool Prefix(IndustryComponent __instance, IIndustryContext ctx)
|
||||||
|
{
|
||||||
|
Log.Information($"{nameof(SimplePassengerStop_Service_Patch)} => {((IndustryContext)ctx)._industry.name}");
|
||||||
|
return IndustryComponent_Service_Patch.Prefix(__instance, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class IndustryComponent_Service_Patch
|
||||||
|
{
|
||||||
|
public static Load CrewHoursLoad()
|
||||||
|
{
|
||||||
|
Load load = (Load)ScriptableObject.CreateInstance(typeof(Load));
|
||||||
|
load.name = "crew-hours";
|
||||||
|
load.description = "Crew";
|
||||||
|
load.units = LoadUnits.Quantity;
|
||||||
|
|
||||||
|
return load;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Prefix(IndustryComponent __instance, IIndustryContext ctx)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!StateManager.IsHost || !tweaksAndThings.IsEnabled || !(tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false)) return true;
|
||||||
|
|
||||||
|
Load load = CrewHoursLoad();
|
||||||
|
|
||||||
|
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 quantityToLoad = Mathf.Min(num2, IndustryComponent.RateToValue(rate2, ctx.DeltaTime));
|
||||||
|
|
||||||
|
var carsAtPosition = ctx.CarsAtPosition();
|
||||||
|
|
||||||
|
var cabeese = from car in carsAtPosition.Where(c => c.CarType == "NE")
|
||||||
|
where car.IsEmptyOrContains(load)
|
||||||
|
orderby car.QuantityOfLoad(load).quantity descending
|
||||||
|
select car;
|
||||||
|
|
||||||
|
foreach (IOpsCar item in cabeese)
|
||||||
|
{
|
||||||
|
TrainController tc = UnityEngine.Object.FindAnyObjectByType<TrainController>();
|
||||||
|
if (tc.TryGetCarForId(item.Id, out Car car))
|
||||||
|
{
|
||||||
|
List<LoadSlot> loadSlots = car.Definition.LoadSlots;
|
||||||
|
float quantity = 0f;
|
||||||
|
float max = 0f;
|
||||||
|
for (int i = 0; i < loadSlots.Count; i++)
|
||||||
|
{
|
||||||
|
LoadSlot loadSlot = loadSlots[i];
|
||||||
|
if (loadSlot.LoadRequirementsMatch(load) && loadSlot.LoadUnits == load.units)
|
||||||
|
{
|
||||||
|
CarLoadInfo? loadInfo = car.GetLoadInfo(i);
|
||||||
|
|
||||||
|
quantity = loadInfo.HasValue ? loadInfo.Value.Quantity : 0f;
|
||||||
|
max = loadSlots[i].MaximumCapacity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Log.Information($"{nameof(IndustryComponent_Service_Patch)} {car} => {car.StoppedDuration} => {quantityToLoad} => {quantity}/{max}");
|
||||||
|
if (car.StoppedDuration > 30) item.Load(load, quantityToLoad);
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo:crew refresh message?
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
TweaksAndThings/Patches/StateManager_OnDayDidChange_Patch.cs
Normal file
50
TweaksAndThings/Patches/StateManager_OnDayDidChange_Patch.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using Game.Events;
|
||||||
|
using Game.State;
|
||||||
|
using HarmonyLib;
|
||||||
|
using KeyValue.Runtime;
|
||||||
|
using Network;
|
||||||
|
using Railloader;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace RMROC451.TweaksAndThings.Patches;
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(StateManager))]
|
||||||
|
[HarmonyPatch(nameof(StateManager.OnDayDidChange), typeof(TimeDayDidChange))]
|
||||||
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
|
internal class StateManager_OnDayDidChange_Patch
|
||||||
|
{
|
||||||
|
private const string unbilledBrakeCrewDuration = "unbilledBrakeCrewDuration";
|
||||||
|
|
||||||
|
private static void Postfix(StateManager __instance)
|
||||||
|
{
|
||||||
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
if (!tweaksAndThings.IsEnabled || !(tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false)) return;
|
||||||
|
if (StateManager.IsHost) PayAutoBrakeCrewWages(__instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PayAutoBrakeCrewWages(StateManager __instance)
|
||||||
|
{
|
||||||
|
float unbilledRunDuration = UnbilledAutoBrakeCrewRunDuration;
|
||||||
|
int num = Mathf.FloorToInt(unbilledRunDuration / 3600f * 5f);
|
||||||
|
float num2 = (float)num / 5f;
|
||||||
|
float unbilledAutoEngineerRunDuration2 = unbilledRunDuration - num2 * 3600f;
|
||||||
|
if (num > 0)
|
||||||
|
{
|
||||||
|
__instance.ApplyToBalance(-num, Ledger.Category.WagesAI, null, memo: "AI Brake Crew");
|
||||||
|
Multiplayer.Broadcast($"Paid {num:C0} for {num2:F1} hours of Brake Crew services.");
|
||||||
|
UnbilledAutoBrakeCrewRunDuration = unbilledAutoEngineerRunDuration2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float UnbilledAutoBrakeCrewRunDuration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return StateManager.Shared._storage._gameKeyValueObject[unbilledBrakeCrewDuration].FloatValue;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
StateManager.Shared._storage._gameKeyValueObject[unbilledBrakeCrewDuration] = Value.Float(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace RMROC451.TweaksAndThings.Patches;
|
|||||||
[HarmonyPatch(typeof(TagController))]
|
[HarmonyPatch(typeof(TagController))]
|
||||||
[HarmonyPatch(nameof(TagController.UpdateTag), typeof(Car), typeof(TagCallout), typeof(OpsController))]
|
[HarmonyPatch(nameof(TagController.UpdateTag), typeof(Car), typeof(TagCallout), typeof(OpsController))]
|
||||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||||
public class TagController_UpdateTag_Patch
|
internal class TagController_UpdateTag_Patch
|
||||||
{
|
{
|
||||||
private const string tagTitleAndIconDelimeter = "\n<width=100%><align=\"right\">";
|
private const string tagTitleAndIconDelimeter = "\n<width=100%><align=\"right\">";
|
||||||
private const string tagTitleFormat = "<align=left><margin-right={0}.5em>{1}</margin><line-height=0>";
|
private const string tagTitleFormat = "<align=left><margin-right={0}.5em>{1}</margin><line-height=0>";
|
||||||
@@ -20,7 +20,7 @@ public class TagController_UpdateTag_Patch
|
|||||||
private static void Postfix(Car car, TagCallout tagCallout)
|
private static void Postfix(Car car, TagCallout tagCallout)
|
||||||
{
|
{
|
||||||
TagController tagController = UnityEngine.Object.FindObjectOfType<TagController>();
|
TagController tagController = UnityEngine.Object.FindObjectOfType<TagController>();
|
||||||
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||||
|
|
||||||
if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
|
if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
<!-- <MajorVersion>1</MajorVersion> -->
|
<!-- <MajorVersion>1</MajorVersion> -->
|
||||||
<!-- <MinorVersion>0</MinorVersion> -->
|
<!-- <MinorVersion>0</MinorVersion> -->
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="mroc-cabeese.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="mroc-cabeese.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<GameAssembly Include="Assembly-CSharp" />
|
<GameAssembly Include="Assembly-CSharp" />
|
||||||
<GameAssembly Include="Railloader.Interchange" />
|
<GameAssembly Include="Railloader.Interchange" />
|
||||||
@@ -12,12 +20,14 @@
|
|||||||
<GameAssembly Include="KeyValue.Runtime" />
|
<GameAssembly Include="KeyValue.Runtime" />
|
||||||
<GameAssembly Include="Definition" />
|
<GameAssembly Include="Definition" />
|
||||||
<GameAssembly Include="Ops" />
|
<GameAssembly Include="Ops" />
|
||||||
|
<GameAssembly Include="StrangeCustoms" />
|
||||||
|
|
||||||
<GameAssembly Include="UnityEngine.CoreModule" />
|
<GameAssembly Include="UnityEngine.CoreModule" />
|
||||||
<GameAssembly Include="UnityEngine.UI" />
|
<GameAssembly Include="UnityEngine.UI" />
|
||||||
<GameAssembly Include="Unity.TextMeshPro" />
|
<GameAssembly Include="Unity.TextMeshPro" />
|
||||||
|
|
||||||
<GameAssembly Include="System.Net.Http" />
|
<GameAssembly Include="System.Net.Http" />
|
||||||
|
<GameAssembly Include="Core" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Krafs.Publicizer" Version="2.2.1">
|
<PackageReference Include="Krafs.Publicizer" Version="2.2.1">
|
||||||
|
|||||||
@@ -17,21 +17,24 @@ public class Settings
|
|||||||
public Settings(
|
public Settings(
|
||||||
List<WebhookSettings> webhookSettingsList,
|
List<WebhookSettings> webhookSettingsList,
|
||||||
bool handBrakeAndAirTagModifiers,
|
bool handBrakeAndAirTagModifiers,
|
||||||
RosterFuelColumnSettings engineRosterFuelColumnSettings
|
RosterFuelColumnSettings engineRosterFuelColumnSettings,
|
||||||
|
bool endGearHelpersRequirePayment
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
WebhookSettingsList = webhookSettingsList;
|
WebhookSettingsList = webhookSettingsList;
|
||||||
HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers;
|
HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers;
|
||||||
EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings;
|
EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings;
|
||||||
|
EndGearHelpersRequirePayment = endGearHelpersRequirePayment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<WebhookSettings>? WebhookSettingsList;
|
public List<WebhookSettings>? WebhookSettingsList;
|
||||||
public bool HandBrakeAndAirTagModifiers;
|
public bool HandBrakeAndAirTagModifiers;
|
||||||
public RosterFuelColumnSettings? EngineRosterFuelColumnSettings;
|
public RosterFuelColumnSettings? EngineRosterFuelColumnSettings;
|
||||||
|
public bool EndGearHelpersRequirePayment;
|
||||||
|
|
||||||
internal void AddAnotherRow()
|
internal void AddAnotherRow()
|
||||||
{
|
{
|
||||||
WebhookSettingsList = !WebhookSettingsList?.Any() ?? false ? new[] { new WebhookSettings() }.ToList() : new List<WebhookSettings>();
|
WebhookSettingsList ??= new[] { new WebhookSettings() }.ToList();
|
||||||
if (!string.IsNullOrEmpty(WebhookSettingsList.OrderByDescending(wsl => wsl.WebhookUrl).Last().WebhookUrl))
|
if (!string.IsNullOrEmpty(WebhookSettingsList.OrderByDescending(wsl => wsl.WebhookUrl).Last().WebhookUrl))
|
||||||
{
|
{
|
||||||
WebhookSettingsList.Add(new());
|
WebhookSettingsList.Add(new());
|
||||||
@@ -73,4 +76,19 @@ public class RosterFuelColumnSettings
|
|||||||
|
|
||||||
public bool EngineRosterShowsFuelStatusAlways;
|
public bool EngineRosterShowsFuelStatusAlways;
|
||||||
public EngineRosterFuelDisplayColumn EngineRosterFuelStatusColumn;
|
public EngineRosterFuelDisplayColumn EngineRosterFuelStatusColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SettingsExtensions
|
||||||
|
{
|
||||||
|
public static List<WebhookSettings> SanitizeEmptySettings(this IEnumerable<WebhookSettings>? settings)
|
||||||
|
{
|
||||||
|
List<WebhookSettings> output =
|
||||||
|
settings?.Where(s => !string.IsNullOrEmpty(s.WebhookUrl))?.ToList() ??
|
||||||
|
new();
|
||||||
|
|
||||||
|
output.Add(new());
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,10 +11,11 @@ using System.Net.Http;
|
|||||||
using UI.Builder;
|
using UI.Builder;
|
||||||
|
|
||||||
using RMROC451.TweaksAndThings.Enums;
|
using RMROC451.TweaksAndThings.Enums;
|
||||||
|
using RMROC451.TweaksAndThings.Commands;
|
||||||
|
|
||||||
namespace RMROC451.TweaksAndThings;
|
namespace RMROC451.TweaksAndThings;
|
||||||
|
|
||||||
public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHandler, IModTabHandler
|
public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>, IUpdateHandler, IModTabHandler
|
||||||
{
|
{
|
||||||
private HttpClient client;
|
private HttpClient client;
|
||||||
internal HttpClient Client
|
internal HttpClient Client
|
||||||
@@ -28,24 +29,24 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal Settings? settings { get; private set; } = null;
|
internal Settings? settings { get; private set; } = null;
|
||||||
readonly ILogger logger = Log.ForContext<TweaksAndThings>();
|
readonly ILogger logger = Log.ForContext<TweaksAndThingsPlugin>();
|
||||||
IModdingContext moddingContext { get; set; }
|
IModdingContext moddingContext { get; set; }
|
||||||
IModDefinition modDefinition { get; set; }
|
IModDefinition modDefinition { get; set; }
|
||||||
|
|
||||||
static TweaksAndThings()
|
static TweaksAndThingsPlugin()
|
||||||
{
|
{
|
||||||
Log.Information("Hello! Static Constructor was called!");
|
Log.Information("Hello! Static Constructor was called!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public TweaksAndThings(IModdingContext moddingContext, IModDefinition self)
|
public TweaksAndThingsPlugin(IModdingContext moddingContext, IModDefinition self)
|
||||||
{
|
{
|
||||||
this.modDefinition = self;
|
this.modDefinition = self;
|
||||||
|
|
||||||
this.moddingContext = moddingContext;
|
this.moddingContext = moddingContext;
|
||||||
|
|
||||||
logger.Information("Hello! Constructor was called for {modId}/{modVersion}!", self.Id, self.Version);
|
logger.Information("Hello! Constructor was called for {modId}/{modVersion}!", self.Id, self.Version);
|
||||||
|
|
||||||
//moddingContext.RegisterConsoleCommand(new EchoCommand());
|
moddingContext.RegisterConsoleCommand(new EchoCommand());
|
||||||
|
|
||||||
settings = moddingContext.LoadSettingsData<Settings>(self.Id);
|
settings = moddingContext.LoadSettingsData<Settings>(self.Id);
|
||||||
}
|
}
|
||||||
@@ -54,7 +55,7 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
{
|
{
|
||||||
logger.Information("OnEnable() was called!");
|
logger.Information("OnEnable() was called!");
|
||||||
var harmony = new Harmony(modDefinition.Id);
|
var harmony = new Harmony(modDefinition.Id);
|
||||||
harmony.PatchCategory(modDefinition.Id.Replace(".",string.Empty));
|
harmony.PatchCategory(modDefinition.Id.Replace(".", string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnDisable()
|
public override void OnDisable()
|
||||||
@@ -77,6 +78,10 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
if (!settings?.WebhookSettingsList?.Any() ?? true) settings.WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
|
if (!settings?.WebhookSettingsList?.Any() ?? true) settings.WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
|
||||||
if (settings?.EngineRosterFuelColumnSettings == null) settings.EngineRosterFuelColumnSettings = new();
|
if (settings?.EngineRosterFuelColumnSettings == null) settings.EngineRosterFuelColumnSettings = new();
|
||||||
|
|
||||||
|
|
||||||
|
settings.WebhookSettingsList =
|
||||||
|
settings?.WebhookSettingsList.SanitizeEmptySettings();
|
||||||
|
|
||||||
//WebhookUISection(ref builder);
|
//WebhookUISection(ref builder);
|
||||||
//builder.AddExpandingVerticalSpacer();
|
//builder.AddExpandingVerticalSpacer();
|
||||||
WebhooksListUISection(ref builder);
|
WebhooksListUISection(ref builder);
|
||||||
@@ -96,12 +101,12 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
builder.AddDropdown(columns, (int)(settings?.EngineRosterFuelColumnSettings?.EngineRosterFuelStatusColumn ?? EngineRosterFuelDisplayColumn.None),
|
builder.AddDropdown(columns, (int)(settings?.EngineRosterFuelColumnSettings?.EngineRosterFuelStatusColumn ?? EngineRosterFuelDisplayColumn.None),
|
||||||
delegate (int column)
|
delegate (int column)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList(), EngineRosterFuelColumnSettings = new() };
|
if (settings == null) settings = new();
|
||||||
settings.EngineRosterFuelColumnSettings.EngineRosterFuelStatusColumn = (EngineRosterFuelDisplayColumn)column;
|
settings.EngineRosterFuelColumnSettings.EngineRosterFuelStatusColumn = (EngineRosterFuelDisplayColumn)column;
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).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.AddField(
|
||||||
"Always Visible?",
|
"Always Visible?",
|
||||||
@@ -109,7 +114,7 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
() => settings?.EngineRosterFuelColumnSettings?.EngineRosterShowsFuelStatusAlways ?? false,
|
() => settings?.EngineRosterFuelColumnSettings?.EngineRosterShowsFuelStatusAlways ?? false,
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList(), EngineRosterFuelColumnSettings = new() };
|
if (settings == null) settings = new();
|
||||||
settings.EngineRosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways = enabled;
|
settings.EngineRosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways = enabled;
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
}
|
}
|
||||||
@@ -128,12 +133,25 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
() => settings?.HandBrakeAndAirTagModifiers ?? false,
|
() => settings?.HandBrakeAndAirTagModifiers ?? false,
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
if (settings == null) settings = new();
|
||||||
settings.HandBrakeAndAirTagModifiers = enabled;
|
settings.HandBrakeAndAirTagModifiers = enabled;
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).Tooltip("Enable Tag Updates", $"Will add {TextSprites.CycleWaybills} to the car tag title having Air System issues. Also prepends {TextSprites.HandbrakeWheel} if there is a handbrake set.\n\nHolding Left Alt while tags are displayed only shows tag titles that have issues.");
|
).Tooltip("Enable Tag Updates", $"Will add {TextSprites.CycleWaybills} to the car tag title having Air System issues. Also prepends {TextSprites.HandbrakeWheel} if there is a handbrake set.\n\nHolding Left Alt while tags are displayed only shows tag titles that have issues.");
|
||||||
|
|
||||||
|
builder.AddField(
|
||||||
|
"Caboose Use",
|
||||||
|
builder.AddToggle(
|
||||||
|
() => settings?.EndGearHelpersRequirePayment ?? false,
|
||||||
|
delegate (bool enabled)
|
||||||
|
{
|
||||||
|
if (settings == null) settings = new();
|
||||||
|
settings.EndGearHelpersRequirePayment = enabled;
|
||||||
|
builder.Rebuild();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).Tooltip("Enable End Gear Helper Cost", $"Will cost 1 minute of AI Brake Crew & Caboose Crew time per car in the consist when the new inspector buttons are utilized.\n\n1.5x multiplier penalty to AI Brake Crew cost if no sufficiently crewed caboose nearby.\n\nCaboose starts reloading `Crew Hours` at any Team or Repair track (no waybill), after being stationary for 30 seconds.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +170,7 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
() => settings?.WebhookSettingsList[z]?.WebhookEnabled ?? false,
|
() => settings?.WebhookSettingsList[z]?.WebhookEnabled ?? false,
|
||||||
delegate (bool enabled)
|
delegate (bool enabled)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
if (settings == null) settings = new();
|
||||||
settings.WebhookSettingsList[z].WebhookEnabled = enabled;
|
settings.WebhookSettingsList[z].WebhookEnabled = enabled;
|
||||||
settings.AddAnotherRow();
|
settings.AddAnotherRow();
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
@@ -168,7 +186,7 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
settings?.WebhookSettingsList[z]?.RailroadMark,
|
settings?.WebhookSettingsList[z]?.RailroadMark,
|
||||||
delegate (string railroadMark)
|
delegate (string railroadMark)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
if (settings == null) settings = new();
|
||||||
settings.WebhookSettingsList[z].RailroadMark = railroadMark;
|
settings.WebhookSettingsList[z].RailroadMark = railroadMark;
|
||||||
settings.AddAnotherRow();
|
settings.AddAnotherRow();
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
@@ -184,7 +202,7 @@ public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHand
|
|||||||
settings?.WebhookSettingsList[z]?.WebhookUrl,
|
settings?.WebhookSettingsList[z]?.WebhookUrl,
|
||||||
delegate (string webhookUrl)
|
delegate (string webhookUrl)
|
||||||
{
|
{
|
||||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
if (settings == null) settings = new();
|
||||||
settings.WebhookSettingsList[z].WebhookUrl = webhookUrl;
|
settings.WebhookSettingsList[z].WebhookUrl = webhookUrl;
|
||||||
settings.AddAnotherRow();
|
settings.AddAnotherRow();
|
||||||
builder.Rebuild();
|
builder.Rebuild();
|
||||||
23
TweaksAndThings/mroc-cabeese.json
Normal file
23
TweaksAndThings/mroc-cabeese.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"$find": [
|
||||||
|
{
|
||||||
|
"path": "definition.archetype",
|
||||||
|
"value": "Caboose"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"definition": {
|
||||||
|
"loadSlots": [
|
||||||
|
{
|
||||||
|
"$add": {
|
||||||
|
"maximumCapacity": 8,
|
||||||
|
"loadUnits": "Quantity",
|
||||||
|
"requiredLoadIdentifier": "crew-hours"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user