mirror of
https://github.com/rmroc451/TweaksAndThings.git
synced 2025-12-17 01:39:38 -06:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f210b0b8a | |||
| 840f35cf62 | |||
| 786db49b68 | |||
| 108900b026 | |||
| 9e8c38e6f4 | |||
| 89f2490f14 | |||
| fbd08b007c | |||
| 27d3432cbf | |||
| cf1d5e32e5 | |||
| 92317b29b2 | |||
| 1f48bf04aa | |||
| 5774c7f04c | |||
| 5eed492b47 | |||
| d4d18d8c92 | |||
| d7e35828b8 | |||
| 0b444d6364 | |||
| d197ff8d8a | |||
| 89894890b4 | |||
| a13701c3d2 | |||
| b5be17703f | |||
| 35afa4520d | |||
| 80d064c950 | |||
| ec21effd30 | |||
| 2242aaacde | |||
| e26713688b | |||
| 8a228be5a8 | |||
| 2ed33465d9 | |||
| 67d39801de | |||
| 8dc87f312d | |||
| 9cb788d86a | |||
| 2e47536028 | |||
| b6edc93636 | |||
| 6693a762c9 | |||
| fc43d54815 | |||
| 0adcbfa787 | |||
| 715629394d | |||
| 1ea3d9917d | |||
| d780b075dc | |||
| d0e2c3f99e | |||
| 8ae319fbee | |||
| 90d4262890 | |||
| a74c004fd4 | |||
| 7a9359ec7d | |||
| 9d9cc96283 | |||
| d7b846a8b2 | |||
| 9cf5998a13 | |||
| ffe6d344d7 | |||
| 7a21c6472e | |||
| a9e6580258 | |||
| b12f3b07c4 | |||
| 371cfc41c8 | |||
| 180b63a41d | |||
| b3546f433d | |||
| ced72acf7c | |||
| 3d1fa908b9 | |||
| 1de82c130b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -396,4 +396,3 @@ FodyWeavers.xsd
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
/TweaksAndThings/Commands/EchoCommand.cs
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
<!-- Copy the mod to the game directory -->
|
||||
<GameModDir Condition="'$(GameModDir)' == ''">$(GameDir)/Mods/$(AssemblyName)</GameModDir>
|
||||
<OutDir Condition="'$(Configuration)' == 'Debug'">$(GameModDir)/</OutDir>
|
||||
<VersionTimestamp>$([System.DateTime]::UtcNow.ToString(`o`))</VersionTimestamp>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Replace the default version if something was set for it -->
|
||||
<PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''">
|
||||
<MajorVersion Condition="'$(MajorVersion)' == ''">0</MajorVersion>
|
||||
<MajorVersion Condition="'$(MajorVersion)' == ''">1</MajorVersion>
|
||||
<MinorVersion Condition="'$(MinorVersion)' == ''">1</MinorVersion>
|
||||
<PatchVersion Condition="'$(PatchVersion)' == ''">0</PatchVersion>
|
||||
<AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion>
|
||||
@@ -30,7 +31,7 @@
|
||||
<!-- Publish the mod as a neat zip file -->
|
||||
<Target Name="PrepareForPublishing" AfterTargets="AfterBuild" Condition="'$(Configuration)' == 'Release'">
|
||||
<!-- Replace $(AssemblyVersion) with the actual version -->
|
||||
<Exec Command="powershell -Command "(Get-Content '$(OutputPath)Definition.json') -replace '\$\(AssemblyVersion\)', '$(AssemblyVersion)' | Set-Content '$(OutputPath)Definition.json'"" />
|
||||
<Exec Command="powershell -Command "(Get-Content '$(OutputPath)Definition.json') -replace '\$\(AssemblyVersion\)', '$(AssemblyVersion)_$(VersionTimestamp)' | Set-Content '$(OutputPath)Definition.json'"" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ModsDirectory>$(OutputPath)/Mods</ModsDirectory>
|
||||
|
||||
21
LICENSE.txt
21
LICENSE.txt
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
31
README.md
31
README.md
@@ -1,5 +1,5 @@
|
||||
[](https://lbesson.mit-license.org/) [](https://ko-fi.com/rmroc451)
|
||||
# RMROC451's Tweaks and Thinks
|
||||
# RMROC451's Tweaks and Things
|
||||
Please Read this **ENTIRE** README before continuing.
|
||||
|
||||
This is a mod for Railroader which is available on Steam.
|
||||
@@ -17,10 +17,10 @@ This mod requires Railloader by Zamu.
|
||||
2. Drag the zip file onto Railloader.exe and have Railloader install the mod.
|
||||
* as with option 1, this will put a `RMROC451.TweaksAndThings` folder into the Mods folder created in step 1
|
||||
* If you DO NOT have the `RMROC451.TweaksAndThings` folder in the Mods folder after completing ONE of the above steps, you didn't complete the step successfully.
|
||||
3. Run the game and enjoy all of you interchange tracks being used.
|
||||
3. Run the game and enjoy all of the tweaks and things!
|
||||
|
||||
## Notes
|
||||
1. This mod currently supports Railroader verison 2024.4.4. This mod may break in future updates. I will do my best to continue to update this mod.
|
||||
1. This mod currently supports Railroader version 2024.4.4. This mod may break in future updates. I will do my best to continue to update this mod.
|
||||
2. It is possible that the developers of Railroader will implement their own fix for this issue. At such time this mod will be deprecated and no longer maintained.
|
||||
3. As the saying goes, use mods at your own risk.
|
||||
|
||||
@@ -35,7 +35,7 @@ This mod requires Railloader by Zamu.
|
||||
* **hold SHIFT** to only show the tags in the FOV for cars with an issue!
|
||||
3. Discord Webhooks
|
||||
* Allows the console messages to post to a discord webhook. useful for those wanting to keep an eye on 24/7 hosted saves.
|
||||
* Locomotive messages grab the locomotive `Ident.RoadNumber` and check the `CTC Panel Markers` if they exist. If found, they will use the red/green color and embed the locmotive as an image in the message. If no marker is found, it defaults to blue.
|
||||
* Locomotive messages grab the locomotive `Ident.RoadNumber` and check the `CTC Panel Markers` if they exist. If found, they will use the red/green color and embed the locomotive as an image in the message. If no marker is found, it defaults to blue.
|
||||
* Currently, One person per server should have this per discord webhook, otherwise you will get duplicate messages to the webhook.
|
||||
* **Multiple hooks**: Allows for many different webhooks per client to be setup, and filtered to the `Ident.ReportingMark` so you can get messages to different hooks based on what save/server you are playing on.
|
||||
* **Customizable** from the in-game Railloader settings, find `RMROC451.TweaksAndThings`
|
||||
@@ -46,27 +46,4 @@ Yes, these are client side mods. Host doesn't need to have them.
|
||||
### What version of Railroader does this mod work with?
|
||||
2024.4.4
|
||||
|
||||
### RMROC451 TaDo
|
||||
- 
|
||||
- [ ] WebhookNotifier
|
||||
- [X] Move base webclient calling to new WebhookNotifier
|
||||
- [X] Extend WebhookNotifier in DiscordNotifier, to do formating of payload only.
|
||||
- [X] Add Settings UI Elements for webhook url
|
||||
- [ ] create thread per host || save game name || nearest city || locomotive??
|
||||
- [X] store in a settings file per host & save game name?
|
||||
- [ ] inspector button(s)
|
||||
- [X] auto connect glad hands/angle cocks
|
||||
- [X] release/set car brake?
|
||||
- [ ] Cost Ideas
|
||||
- [ ] $$$ based on usage per day? or flat $5 per use?
|
||||
- [ ] Caboose only?
|
||||
- [ ] detect car having a nearby caboose to use? `UpdateCarsNearbyPlayer` but for car.
|
||||
- [ ] add crew to caboose (similar to engine service), $25/day to fill up, must have crew to use and caboose near cars.
|
||||
- [ ] crew number modifier for how many cars away from caboose it can effectively work?
|
||||
- [ ] crew need to refill at a station?
|
||||
- 
|
||||
- [ ] Camera offset to coupler
|
||||
- [ ] shift + 9/0
|
||||
- [ ] finance exporter/discord report at end of day for Game.State.Ledger
|
||||
|
||||
*Special thanks and credit to Zamu for creating Railloader and for help with making the mod a bit more robust.*
|
||||
@@ -5,9 +5,11 @@ VisualStudioVersion = 17.4.33122.133
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{452A23A6-81C8-49C6-A7EE-95FD9377F896}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.gitignore = .gitignore
|
||||
Directory.Build.props = Directory.Build.props
|
||||
Directory.Build.targets = Directory.Build.targets
|
||||
Paths.user = Paths.user
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RMROC451.TweaksAndThings", "TweaksAndThings\RMROC451.TweaksAndThings.csproj", "{59BB5A3F-C26B-4E2D-A657-94C562449365}"
|
||||
|
||||
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",
|
||||
"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)"
|
||||
}
|
||||
}
|
||||
9
TweaksAndThings/Enums/EngineRosterFuelDisplayColumn.cs
Normal file
9
TweaksAndThings/Enums/EngineRosterFuelDisplayColumn.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace RMROC451.TweaksAndThings.Enums;
|
||||
|
||||
public enum EngineRosterFuelDisplayColumn
|
||||
{
|
||||
None,
|
||||
Engine,
|
||||
Crew,
|
||||
Status
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace TweaksAndThings.Enums;
|
||||
namespace RMROC451.TweaksAndThings.Enums;
|
||||
|
||||
public enum MrocHelperType
|
||||
{
|
||||
Handbrake,
|
||||
GladhandAndAnglecock
|
||||
GladhandAndAnglecock,
|
||||
BleedAirSystem
|
||||
}
|
||||
|
||||
118
TweaksAndThings/Extensions/AutoEngineer_Extensions.cs
Normal file
118
TweaksAndThings/Extensions/AutoEngineer_Extensions.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Model.AI;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Extensions
|
||||
{
|
||||
internal static class AutoEngineer_Extensions
|
||||
{
|
||||
private static float CabooseHalvedFloat(this float input, Model.Car? hasCaboose) =>
|
||||
hasCaboose ? input / 2 : input;
|
||||
|
||||
private static float CabooseAutoOilerLimit(this Model.Car? caboose) =>
|
||||
caboose ? 0.99f : AutoOiler.OilIfBelow;
|
||||
|
||||
public static IEnumerator MrocAutoOilerLoop(this AutoOiler oiler, Serilog.ILogger _log, bool cabooseRequired)
|
||||
{
|
||||
int originIndex = oiler.FindOriginIndex();
|
||||
Model.Car? foundCaboose = oiler._cars.CabooseInConsist();
|
||||
if (originIndex < 0)
|
||||
{
|
||||
_log.Error("Couldn't find origin car {car}", oiler._originCar);
|
||||
oiler._coroutine = null;
|
||||
yield break;
|
||||
} else if (CabooseRequirementChecker(string.Format("{0} {1}", oiler.GetType().Name, oiler.name), cabooseRequired, foundCaboose, _log))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
oiler._reverse = originIndex > oiler._cars.Count - originIndex;
|
||||
_log.Information(
|
||||
"AutoOiler {name} starting, rev = {reverse}, caboose required = {req}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}",
|
||||
oiler.name,
|
||||
oiler._reverse,
|
||||
cabooseRequired,
|
||||
foundCaboose,
|
||||
foundCaboose.CabooseAutoOilerLimit()
|
||||
);
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForSeconds(AutoOiler.StartDelay.CabooseHalvedFloat(foundCaboose));
|
||||
int carIndex = originIndex;
|
||||
float adjustedTimeToWalk = AutoOiler.TimeToWalkCar.CabooseHalvedFloat(foundCaboose);
|
||||
do
|
||||
{
|
||||
if (oiler.TryGetCar(carIndex, out var car))
|
||||
{
|
||||
float num = 0f;
|
||||
float origOil = car.Oiled;
|
||||
if (car.NeedsOiling && car.Oiled < foundCaboose.CabooseAutoOilerLimit())
|
||||
{
|
||||
float num2 = 1f - car.Oiled;
|
||||
car.OffsetOiled(num2);
|
||||
float num3 = num2 * AutoOiler.TimeToFullyOil.CabooseHalvedFloat(foundCaboose);
|
||||
num += num3;
|
||||
oiler._pendingRunDuration += num3;
|
||||
oiler._oiledCount++;
|
||||
_log.Information("AutoOiler {name}: oiled {car} from {orig} => {new}", oiler.name, car, origOil, car.Oiled);
|
||||
}
|
||||
if (car.HasHotbox && car.Oiled == 1f && cabooseRequired && foundCaboose)
|
||||
{
|
||||
_log.Information("AutoOiler {name}: {foundCaboose} repaired hotbox {car}", oiler.name, foundCaboose, car);
|
||||
car.AdjustHotboxValue(0f);
|
||||
}
|
||||
num += adjustedTimeToWalk;
|
||||
oiler._pendingRunDuration += adjustedTimeToWalk;
|
||||
yield return new WaitForSeconds(num);
|
||||
}
|
||||
carIndex = oiler.NextIndex(carIndex);
|
||||
}
|
||||
while (oiler.InBounds(carIndex));
|
||||
oiler._reverse = !oiler._reverse;
|
||||
oiler.PayWages();
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator MrocAutoHotboxSpotterLoop(this AutoHotboxSpotter spotter, Serilog.ILogger _log, bool cabooseRequired)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Model.Car? foundCaboose = spotter._cars.CabooseInConsist();
|
||||
if (!spotter.HasCars)
|
||||
{
|
||||
yield return new WaitForSeconds(1f);
|
||||
continue;
|
||||
}
|
||||
_log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Found Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}",
|
||||
spotter.name, foundCaboose, spotter.HasCars, cabooseRequired);
|
||||
foundCaboose = spotter._cars.CabooseInConsist();
|
||||
if (CabooseRequirementChecker(string.Format("{0} {1}", spotter.GetType().Name, spotter.name), cabooseRequired, foundCaboose, _log))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
spotter.CheckForHotbox();
|
||||
while (spotter.HasCars)
|
||||
{
|
||||
int num = Random.Range(60, 300);
|
||||
foundCaboose = spotter._cars.CabooseInConsist();
|
||||
if (foundCaboose)
|
||||
{
|
||||
var numOrig = num;
|
||||
num = Random.Range(15, 30);
|
||||
_log.Information("AutoHotboxSpotter {name}: Next check went from num(60,300) => {numOrig}; to num(15,30) => {hasCaboose}; Requires Caboose {requiresCaboose}", spotter.name, numOrig, num, foundCaboose, cabooseRequired);
|
||||
}
|
||||
yield return new WaitForSeconds(num);
|
||||
spotter.CheckForHotbox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CabooseRequirementChecker(string name, bool cabooseRequired, Model.Car? foundCaboose, Serilog.ILogger _log)
|
||||
{
|
||||
bool error = cabooseRequired && foundCaboose == null;
|
||||
if (error) {
|
||||
_log.Debug("{name}: Couldn't find required caboose!", name);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
137
TweaksAndThings/Extensions/Car_Extensions.cs
Normal file
137
TweaksAndThings/Extensions/Car_Extensions.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using Helpers;
|
||||
using Model;
|
||||
using Model.Definition.Data;
|
||||
using Model.OpsNew;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Extensions;
|
||||
|
||||
public static class Car_Extensions
|
||||
{
|
||||
private static bool EndGearIssue(this Car car, Car.LogicalEnd end) =>
|
||||
(!car[end].IsCoupled && car[end].IsAnglecockOpen) ||
|
||||
(car[end].IsCoupled && !car[end].IsAirConnectedAndOpen);
|
||||
|
||||
public static bool EndAirSystemIssue(this Car car)
|
||||
{
|
||||
bool AEndAirSystemIssue = car.EndGearIssue(Car.LogicalEnd.A);
|
||||
bool BEndAirSystemIssue = car.EndGearIssue(Car.LogicalEnd.B);
|
||||
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)
|
||||
{
|
||||
car = steamLocomotive.FuelCar();
|
||||
}
|
||||
else
|
||||
{
|
||||
car = engine is DieselLocomotive ? engine : null;
|
||||
if (returnEngineIfNull && car == null) car = engine;
|
||||
}
|
||||
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 IsCabooseAndStoppedForLoadRefresh(this Car car) => car.IsCaboose() && car.IsStopped(30f);
|
||||
|
||||
public static Car? CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(c => c.IsCaboose());
|
||||
|
||||
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, HashSet<string> carIdsCheckedAlready, bool decrement = false)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public static void AdjustHotboxValue(this Car car, float hotboxValue) =>
|
||||
StateManager.ApplyLocal(
|
||||
new PropertyChange(
|
||||
car.id, PropertyChange.KeyForControl(PropertyChange.Control.Hotbox),
|
||||
new FloatPropertyValue(hotboxValue)
|
||||
)
|
||||
);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using HarmonyLib;
|
||||
using Model.AI;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using Serilog;
|
||||
using System.Collections;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(AutoHotboxSpotter))]
|
||||
[HarmonyPatch(nameof(AutoHotboxSpotter.SpotterLoop))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class AutoHotboxSpotter_SpotterLoop_Patch
|
||||
{
|
||||
private static ILogger _log => Log.ForContext<AutoHotboxSpotter_SpotterLoop_Patch>();
|
||||
|
||||
public static bool Prefix(AutoHotboxSpotter __instance, ref IEnumerator __result)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled) return true;
|
||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
|
||||
|
||||
if (buttonsHaveCost) __result = __instance.MrocAutoHotboxSpotterLoop(_log, cabooseRequired);
|
||||
return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
|
||||
}
|
||||
}
|
||||
27
TweaksAndThings/Patches/AutoOiler_Loop_Patch.cs
Normal file
27
TweaksAndThings/Patches/AutoOiler_Loop_Patch.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using HarmonyLib;
|
||||
using Model.AI;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using Serilog;
|
||||
using System.Collections;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(AutoOiler))]
|
||||
[HarmonyPatch(nameof(AutoOiler.Loop))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class AutoOiler_Loop_Patch
|
||||
{
|
||||
private static ILogger _log => Log.ForContext<AutoOiler_Loop_Patch>();
|
||||
|
||||
public static bool Prefix(AutoOiler __instance, ref IEnumerator __result)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled) return true;
|
||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||
bool cabooseRequired = tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter();
|
||||
|
||||
if (buttonsHaveCost) __result = __instance.MrocAutoOilerLoop(_log, cabooseRequired);
|
||||
return !buttonsHaveCost; //only hit this if !buttonsHaveCost, since Loop is a coroutine
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,33 @@
|
||||
using GalaSoft.MvvmLight.Messaging;
|
||||
using Game;
|
||||
using Game.Events;
|
||||
using Core;
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using KeyValue.Runtime;
|
||||
using Model;
|
||||
using Network;
|
||||
using Network.Messages;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Enums;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RollingStock;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using TweaksAndThings.Enums;
|
||||
using UI;
|
||||
using UI.Builder;
|
||||
using UI.CarInspector;
|
||||
using WorldStreamer2;
|
||||
using UI.ContextMenu;
|
||||
using UI.Tags;
|
||||
using static Model.Car;
|
||||
using static Unity.IO.LowLevel.Unsafe.AsyncReadManagerMetrics;
|
||||
|
||||
namespace TweaksAndThings.Patches;
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(CarInspector))]
|
||||
[HarmonyPatch(nameof(CarInspector.PopulateCarPanel), typeof(UIPanelBuilder))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
public class CarInspector_PopulateCarPanel_Patch
|
||||
internal class CarInspector_PopulateCarPanel_Patch
|
||||
{
|
||||
private static ILogger _log => Log.ForContext<CarInspector_PopulateCarPanel_Patch>();
|
||||
private static IEnumerable<LogicalEnd> ends = Enum.GetValues(typeof(LogicalEnd)).Cast<LogicalEnd>();
|
||||
|
||||
/// <summary>
|
||||
@@ -41,36 +39,136 @@ public class CarInspector_PopulateCarPanel_Patch
|
||||
private static bool Prefix(CarInspector __instance, UIPanelBuilder builder)
|
||||
{
|
||||
|
||||
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled) return true;
|
||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||
|
||||
var consist = __instance._car.EnumerateCoupled();
|
||||
builder = AddCarConsistRebuildObservers(builder, consist);
|
||||
|
||||
builder.HStack(delegate (UIPanelBuilder hstack)
|
||||
{
|
||||
hstack.AddButtonCompact("Handbrakes", delegate
|
||||
var buttonName = $"{(consist.Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} {TextSprites.HandbrakeWheel}";
|
||||
hstack.AddButtonCompact(buttonName, delegate
|
||||
{
|
||||
MrocConsistHelper(__instance._car, MrocHelperType.Handbrake);
|
||||
}).Tooltip("Release Consist Handbrakes", "Iterates over each car in this consist and releases handbrakes.");
|
||||
if (StateManager.IsHost || true)
|
||||
MrocConsistHelper(__instance._car, MrocHelperType.Handbrake, buttonsHaveCost);
|
||||
hstack.Rebuild();
|
||||
}).Tooltip(buttonName, $"Iterates over cars in this consist and {(consist.Any(c => c.HandbrakeApplied()) ? "releases" : "sets")} {TextSprites.HandbrakeWheel}.");
|
||||
|
||||
if (consist.Any(c => c.EndAirSystemIssue()))
|
||||
{
|
||||
hstack.AddButtonCompact("Air Lines", delegate
|
||||
hstack.AddButtonCompact("Connect Air", delegate
|
||||
{
|
||||
MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock);
|
||||
}).Tooltip("Connect Consist Air Lines", "Iterates over each car in this consist and connects gladhands and opens anglecocks.");
|
||||
MrocConsistHelper(__instance._car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
|
||||
hstack.Rebuild();
|
||||
}).Tooltip("Connect Consist Air", "Iterates over each car in this consist and connects gladhands and opens anglecocks.");
|
||||
}
|
||||
|
||||
hstack.AddButtonCompact("Bleed Consist", delegate
|
||||
{
|
||||
MrocConsistHelper(__instance._car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
|
||||
hstack.Rebuild();
|
||||
}).Tooltip("Bleed Air Lines", "Iterates over each car in this consist and bleeds the air out of the lines.");
|
||||
});
|
||||
|
||||
CabooseUiEnhancer(__instance, builder, consist, tweaksAndThings);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType)
|
||||
private static void CabooseUiEnhancer(CarInspector __instance, UIPanelBuilder builder, IEnumerable<Car> consist, TweaksAndThingsPlugin plugin)
|
||||
{
|
||||
IEnumerable<Model.Car> consist = car.EnumerateCoupled(LogicalEnd.A);
|
||||
if (plugin.CabooseNonMotiveAllowedSetting(__instance._car))
|
||||
{
|
||||
builder.HStack(delegate (UIPanelBuilder hstack)
|
||||
{
|
||||
hstack.AddField("Consist Info", hstack.HStack(delegate (UIPanelBuilder field)
|
||||
{
|
||||
int consistLength = consist.Count();
|
||||
int tonnage = LocomotiveControlsHoverArea.CalculateTonnage(consist);
|
||||
int lengthInMeters = UnityEngine.Mathf.CeilToInt(LocomotiveControlsHoverArea.CalculateLengthInMeters(consist.ToList()) * 3.28084f);
|
||||
var newSubTitle = () => string.Format("{0}, {1:N0}T, {2:N0}ft, {3:0.0} mph", consistLength.Pluralize("car"), tonnage, lengthInMeters, __instance._car.VelocityMphAbs);
|
||||
|
||||
field.AddLabel(() => newSubTitle(), UIPanelBuilder.Frequency.Fast)
|
||||
.Tooltip("Consist Info", "Reflects info about consist.").FlexibleWidth();
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static UIPanelBuilder AddCarConsistRebuildObservers(UIPanelBuilder builder, IEnumerable<Model.Car> consist)
|
||||
{
|
||||
TagController tagController = UnityEngine.Object.FindFirstObjectByType<TagController>();
|
||||
foreach (Model.Car car in consist.Where(c => c.Archetype != Model.Definition.CarArchetype.Tender))
|
||||
{
|
||||
builder = AddObserver(builder, car, PropertyChange.KeyForControl(PropertyChange.Control.Handbrake), tagController);
|
||||
foreach (LogicalEnd logicalEnd in ends)
|
||||
{
|
||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsCoupled, car.LogicalToEnd(logicalEnd)), tagController);
|
||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.IsAirConnected, car.LogicalToEnd(logicalEnd)), tagController);
|
||||
builder = AddObserver(builder, car, KeyValueKeyFor(EndGearStateKey.Anglecock, car.LogicalToEnd(logicalEnd)), tagController);
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static UIPanelBuilder AddObserver(UIPanelBuilder builder, Model.Car car, string key, TagController tagController)
|
||||
{
|
||||
builder.AddObserver(
|
||||
car.KeyValueObject.Observe(
|
||||
key,
|
||||
delegate (Value value)
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.RebuildOnInterval(.01f);
|
||||
if (car.TagCallout != null) tagController.UpdateTags(CameraSelector.shared._currentCamera.GroundPosition, true);
|
||||
if (ContextMenu.IsShown && ContextMenu.Shared.centerLabel.text == car.DisplayName) CarPickable.HandleShowContextMenu(car);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}");
|
||||
}
|
||||
},
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
//var dh = new DownloadHandlerAudioClip($"file://{cacheFileName}", AudioType.MPEG);
|
||||
//dh.compressed = true; // This
|
||||
|
||||
//using (UnityWebRequest wr = new UnityWebRequest($"file://{cacheFileName}", "GET", dh, null)) {
|
||||
// yield return wr.SendWebRequest();
|
||||
// if (wr.responseCode == 200) {
|
||||
// audioSource.clip = dh.audioClip;
|
||||
// }
|
||||
//}
|
||||
|
||||
public static void MrocConsistHelper(Model.Car car, MrocHelperType mrocHelperType, bool buttonsHaveCost)
|
||||
{
|
||||
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
|
||||
IEnumerable<Model.Car> consist = car.EnumerateCoupled();
|
||||
_log.ForContext("car", car).Verbose($"{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)
|
||||
{
|
||||
case MrocHelperType.Handbrake:
|
||||
if (consist.Any(c => c.HandbrakeApplied()))
|
||||
{
|
||||
consist.Do(c => c.SetHandbrake(false));
|
||||
}
|
||||
else
|
||||
{
|
||||
consist = consist.Where(c => c is not BaseLocomotive && c.Archetype != Model.Definition.CarArchetype.Tender);
|
||||
//when ApplyHandbrakesAsNeeded is called, and the consist contains an engine, it stops applying brakes.
|
||||
tc.ApplyHandbrakesAsNeeded(consist.ToList(), PlaceTrainHandbrakes.Automatic);
|
||||
}
|
||||
break;
|
||||
|
||||
case MrocHelperType.GladhandAndAnglecock:
|
||||
@@ -87,14 +185,66 @@ public class CarInspector_PopulateCarPanel_Patch
|
||||
)
|
||||
);
|
||||
|
||||
if (c.TryGetAdjacentCar(end, out Model.Car c2))
|
||||
{
|
||||
StateManager.ApplyLocal(new SetGladhandsConnected(c.id, c2.id, true));
|
||||
}
|
||||
|
||||
if (c.TryGetAdjacentCar(end, out Model.Car c2)) StateManager.ApplyLocal(new SetGladhandsConnected(c.id, c2.id, true));
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case MrocHelperType.BleedAirSystem:
|
||||
consist = consist.Where(c => c.NotMotivePower());
|
||||
_log.ForContext("car", car).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(OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad().description);
|
||||
Car? cabooseWithAvailCrew = NearbyCabooseWithAvailableCrew(car, crewCost, buttonsHaveCost);
|
||||
if (cabooseWithAvailCrew == null) timeCost *= 1.5f;
|
||||
var cabooseFoundDisplay = cabooseWithAvailCrew?.DisplayName ?? "No caboose";
|
||||
|
||||
_log.ForContext("car", car).Information($"{nameof(MrocConsistHelper)} {mrocHelperType} : [VACINITY CABEESE FOUND:{cabooseWithAvailCrew?.ToString() ?? "NONE"}] => Consist Length {consist.Count()} => costs {timeCost / 60} minutes of AI Engineer time, $5 per hour = ~${Math.Ceiling((decimal)(timeCost / 3600) * 5)} (*2 if no caboose nearby)");
|
||||
|
||||
|
||||
Multiplayer.SendError(StateManager.Shared._playersManager.LocalPlayer, $"{(cabooseWithAvailCrew != null ? $"{cabooseWithAvailCrew.DisplayName} Hours Adjusted: ({tsString})\n" : string.Empty)}Wages: ~(${Math.Ceiling((decimal)(timeCost / 3600) * 5)})");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
52
TweaksAndThings/Patches/CarPickable_Activate_Patch.cs
Normal file
52
TweaksAndThings/Patches/CarPickable_Activate_Patch.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using HarmonyLib;
|
||||
using Railloader;
|
||||
using RollingStock;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(CarPickable))]
|
||||
[HarmonyPatch(nameof(CarPickable.Activate), typeof(PickableActivateEvent))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class CarPickable_Activate_Patch
|
||||
{
|
||||
|
||||
static float clicked = 0;
|
||||
static float clicktime = 0;
|
||||
static float clickdelay = 0.5f;
|
||||
|
||||
private static bool Prefix(CarPickable __instance, PickableActivateEvent evt)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled) return true;
|
||||
|
||||
if (OnPointerDown(evt))
|
||||
{
|
||||
CameraSelector.shared.FollowCar(__instance.car);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool OnPointerDown(PickableActivateEvent evt)
|
||||
{
|
||||
bool output = false;
|
||||
if (evt.Activation == PickableActivation.Primary)
|
||||
{
|
||||
clicked++;
|
||||
if (clicked == 1) clicktime = Time.time;
|
||||
|
||||
if (clicked > 1 && Time.time - clicktime < clickdelay)
|
||||
{
|
||||
clicked = 0;
|
||||
clicktime = 0;
|
||||
output = true;
|
||||
|
||||
}
|
||||
else if (clicked > 2 || Time.time - clicktime > 1) clicked = 0;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using HarmonyLib;
|
||||
using Model;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Enums;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RollingStock;
|
||||
using System.Linq;
|
||||
using UI;
|
||||
using UI.ContextMenu;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(CarPickable))]
|
||||
[HarmonyPatch(nameof(CarPickable.HandleShowContextMenu), typeof(Car))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class CarPickable_HandleShowContextMenu_Patch
|
||||
{
|
||||
private static void Postfix(Car car)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!tweaksAndThings.IsEnabled) return;
|
||||
|
||||
bool buttonsHaveCost = tweaksAndThings.EndGearHelpersRequirePayment();
|
||||
ContextMenu shared = ContextMenu.Shared;
|
||||
shared.AddButton(ContextMenuQuadrant.Unused2, $"{(car.EnumerateCoupled().Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate
|
||||
{
|
||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost);
|
||||
});
|
||||
|
||||
if (car.EnumerateCoupled().Any(c => c.EndAirSystemIssue()))
|
||||
{
|
||||
shared.AddButton(ContextMenuQuadrant.Unused2, $"Air Up Consist", SpriteName.Select, delegate
|
||||
{
|
||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, buttonsHaveCost);
|
||||
});
|
||||
}
|
||||
|
||||
if (car.EnumerateCoupled().Any(c => c.SupportsBleed()))
|
||||
{
|
||||
shared.AddButton(ContextMenuQuadrant.Unused2, $"Bleed Consist", SpriteName.Bleed, delegate
|
||||
{
|
||||
CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.BleedAirSystem, buttonsHaveCost);
|
||||
});
|
||||
}
|
||||
|
||||
shared.AddButton(ContextMenuQuadrant.Unused2, $"Follow", SpriteName.Inspect, delegate
|
||||
{
|
||||
CameraSelector.shared.FollowCar(car);
|
||||
});
|
||||
|
||||
shared.BuildItemAngles();
|
||||
shared.StartCoroutine(shared.AnimateButtonsShown());
|
||||
}
|
||||
}
|
||||
100
TweaksAndThings/Patches/EngineRosterRow_Refresh_Patch.cs
Normal file
100
TweaksAndThings/Patches/EngineRosterRow_Refresh_Patch.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using HarmonyLib;
|
||||
using Model;
|
||||
using Model.Definition.Data;
|
||||
using Model.OpsNew;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TMPro;
|
||||
using RMROC451.TweaksAndThings.Enums;
|
||||
using UI;
|
||||
using UI.EngineRoster;
|
||||
using UI.Tooltips;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(EngineRosterRow))]
|
||||
[HarmonyPatch(nameof(EngineRosterRow.Refresh))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class EngineRosterRow_Refresh_Patch
|
||||
{
|
||||
public static void Postfix(EngineRosterRow __instance)
|
||||
{
|
||||
TweaksAndThingsPlugin? tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
RosterFuelColumnSettings? rosterFuelColumnSettings = tweaksAndThings?.settings?.EngineRosterFuelColumnSettings;
|
||||
|
||||
if (tweaksAndThings == null ||
|
||||
rosterFuelColumnSettings == null ||
|
||||
!tweaksAndThings.IsEnabled ||
|
||||
rosterFuelColumnSettings.EngineRosterFuelStatusColumn == EngineRosterFuelDisplayColumn.None || (!GameInput.IsAltDown && !rosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string fuelInfoText = string.Empty;
|
||||
string fuelInfoTooltip = string.Empty;
|
||||
Car engineOrTender = __instance._engine;
|
||||
List<LoadSlot> loadSlots = __instance._engine.Definition.LoadSlots;
|
||||
if (!loadSlots.Any())
|
||||
{
|
||||
engineOrTender = __instance._engine.DetermineFuelCar()!;
|
||||
loadSlots = engineOrTender != null ? engineOrTender.Definition.LoadSlots : Enumerable.Empty<LoadSlot>().ToList();
|
||||
}
|
||||
|
||||
var offender = loadSlots.OrderBy(ls => (engineOrTender.GetLoadInfo(ls.RequiredLoadIdentifier, out int slotIndex)?.Quantity ?? 0) / loadSlots[slotIndex].MaximumCapacity).FirstOrDefault().RequiredLoadIdentifier;
|
||||
|
||||
for (int i = 0; i < loadSlots.Count; i++)
|
||||
{
|
||||
CarLoadInfo? loadInfo = engineOrTender.GetLoadInfo(i);
|
||||
if (loadInfo.HasValue)
|
||||
{
|
||||
CarLoadInfo valueOrDefault = loadInfo.GetValueOrDefault();
|
||||
var fuelLevel = FuelLevel(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity);
|
||||
fuelInfoText += loadSlots[i].RequiredLoadIdentifier == offender ? fuelLevel + " " : string.Empty;
|
||||
//fuelInfoText += TextSprites.PiePercent(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity) + " ";
|
||||
fuelInfoTooltip += $"{TextSprites.PiePercent(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity)} {valueOrDefault.LoadString(CarPrototypeLibrary.instance.LoadForId(valueOrDefault.LoadId))}\n";
|
||||
}
|
||||
}
|
||||
|
||||
switch (rosterFuelColumnSettings.EngineRosterFuelStatusColumn)
|
||||
{
|
||||
case EngineRosterFuelDisplayColumn.Engine:
|
||||
SetLabelAndTooltip(ref __instance.nameLabel, ref __instance.nameTooltip, fuelInfoText, fuelInfoTooltip);
|
||||
break;
|
||||
case EngineRosterFuelDisplayColumn.Crew:
|
||||
SetLabelAndTooltip(ref __instance.crewLabel, ref __instance.crewTooltip, fuelInfoText, fuelInfoTooltip);
|
||||
break;
|
||||
case EngineRosterFuelDisplayColumn.Status:
|
||||
SetLabelAndTooltip(ref __instance.infoLabel, ref __instance.infoTooltip, fuelInfoText, fuelInfoTooltip);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (Exception ex)
|
||||
{
|
||||
rosterFuelColumnSettings.EngineRosterFuelStatusColumn = EngineRosterFuelDisplayColumn.None;
|
||||
Log.Error(ex, "Error Detecting fuel status for engine roster");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetLabelAndTooltip(ref TMP_Text label, ref UITooltipProvider tooltip, string fuelInfoText, string fuelInfoTooltip)
|
||||
{
|
||||
label.text = $" {fuelInfoText} {label.text}";
|
||||
tooltip.TooltipInfo = new TooltipInfo(tooltip.tooltipTitle, fuelInfoTooltip);
|
||||
}
|
||||
|
||||
public static string FuelLevel(float quantity, float capacity)
|
||||
{
|
||||
float num = capacity <= 0f ? 0 : Mathf.Clamp(quantity / capacity * 100, 0, 100);
|
||||
|
||||
return $"{Mathf.FloorToInt(num):D2}%";
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,12 @@ using System.Text.RegularExpressions;
|
||||
using Track.Signals.Panel;
|
||||
using UI.Console;
|
||||
|
||||
namespace TweaksAndThings.Patches;
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(ExpandedConsole))]
|
||||
[HarmonyPatch(nameof(ExpandedConsole.Add))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
public class ExpandedConsole_Add_Patch
|
||||
internal class ExpandedConsole_Add_Patch
|
||||
{
|
||||
private static void Prefix(ref UI.Console.Console.Entry entry)
|
||||
{
|
||||
@@ -32,12 +32,10 @@ public class ExpandedConsole_Add_Patch
|
||||
{
|
||||
try
|
||||
{
|
||||
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
StateManager shared = StateManager.Shared;
|
||||
GameStorage gameStorage = shared.Storage;
|
||||
WebhookSettings settings = tweaksAndThings.settings.WebhookSettingsList.FirstOrDefault(ws => ws.RailroadMark == gameStorage.RailroadMark);
|
||||
|
||||
Log.Information(entry.Text);
|
||||
WebhookSettings settings = tweaksAndThings?.settings?.WebhookSettingsList?.FirstOrDefault(ws => ws.RailroadMark == gameStorage.RailroadMark);
|
||||
|
||||
if (
|
||||
settings != null &&
|
||||
@@ -50,7 +48,6 @@ public class ExpandedConsole_Add_Patch
|
||||
|
||||
var carId = t.IsMatch(entry.Text) ? Regex.Match(entry.Text, "car:(.*?)\"").Groups[1].Captures[0].ToString() : string.Empty;
|
||||
Model.Car? car = TrainController.Shared.CarForString(carId);
|
||||
Log.Information($"|{carId}| {car?.IsLocomotive}");
|
||||
bool engineInMessage = car?.IsLocomotive ?? false;
|
||||
var image = engineInMessage ?
|
||||
new
|
||||
@@ -91,7 +88,6 @@ public class ExpandedConsole_Add_Patch
|
||||
}
|
||||
};
|
||||
string EndPoint = settings.WebhookUrl;
|
||||
Log.Information(JsonConvert.SerializeObject(SuccessWebHook));
|
||||
|
||||
var content = new StringContent(JsonConvert.SerializeObject(SuccessWebHook), Encoding.UTF8, "application/json");
|
||||
|
||||
@@ -102,7 +98,6 @@ public class ExpandedConsole_Add_Patch
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, ex.Message);
|
||||
entry.Text += ex.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
using Game;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using Model;
|
||||
using Model.Definition.Data;
|
||||
using Model.Ops.Definition;
|
||||
using Model.OpsNew;
|
||||
using Network;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using RollingStock;
|
||||
using Serilog;
|
||||
using System;
|
||||
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 == OpsController_AnnounceCoalescedPayments_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 = OpsController_AnnounceCoalescedPayments_Patch.CrewHoursLoad();
|
||||
if (loadId == load.id) __result = load;
|
||||
|
||||
return __result == null;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(OpsController))]
|
||||
[HarmonyPatch(nameof(OpsController.AnnounceCoalescedPayments))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class OpsController_AnnounceCoalescedPayments_Patch
|
||||
{
|
||||
static Dictionary<string, (bool spotted, bool filling)> CrewCarDict = [];
|
||||
public static (bool spotted, bool filling) CrewCarStatus(Car car)
|
||||
{
|
||||
bool found = CrewCarDict.TryGetValue(car.id, out (bool spotted, bool filling) val);
|
||||
|
||||
if (!found) CrewCarDict.Add(car.id, (false, false));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static GameDateTime dateTime = TimeWeather.Now;
|
||||
static readonly IEnumerable<Type> refillLocations = [
|
||||
typeof(PassengerStop),
|
||||
typeof(SimplePassengerStop),
|
||||
typeof(TeamTrack),
|
||||
typeof(RepairTrack)
|
||||
];
|
||||
|
||||
public static Load CrewHoursLoad()
|
||||
{
|
||||
Load load = (Load)ScriptableObject.CreateInstance(typeof(Load));
|
||||
load.name = "crew-hours";
|
||||
load.description = "Crew";
|
||||
load.units = LoadUnits.Quantity;
|
||||
|
||||
return load;
|
||||
}
|
||||
|
||||
private static void CarLoadCrewHelper(Car car, float deltaTime)
|
||||
{
|
||||
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, deltaTime));
|
||||
|
||||
if (car.IsCaboose() && !CrewCarStatus(car).spotted)
|
||||
{
|
||||
CrewCarDict[car.id] = (true, CrewCarDict[car.id].filling);
|
||||
}
|
||||
if (car.IsCabooseAndStoppedForLoadRefresh())
|
||||
{
|
||||
if (!CrewCarDict[car.id].filling) Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Topping off caboose crew.\"");
|
||||
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, true);
|
||||
var data = car.QuantityCapacityOfLoad(CrewHoursLoad());
|
||||
if ((data.quantity + quantityToLoad > data.capacity) && data.quantity < data.capacity)
|
||||
{
|
||||
Multiplayer.Broadcast($"{Hyperlink.To(car)}: \"Caboose crew topped off.\"");
|
||||
CrewCarDict[car.id] = (CrewCarDict[car.id].spotted, false);
|
||||
}
|
||||
new OpsCarAdapter(car, OpsController.Shared).Load(CrewHoursLoad(), quantityToLoad);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Prefix(IndustryComponent __instance)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
if (!StateManager.IsHost || !tweaksAndThings.IsEnabled || !(tweaksAndThings?.settings?.EndGearHelpersRequirePayment ?? false)) return true;
|
||||
|
||||
|
||||
TrainController tc = UnityEngine.Object.FindAnyObjectByType<TrainController>();
|
||||
try {
|
||||
|
||||
var passengerStops = OpsController.Shared.AllIndustries
|
||||
.SelectMany(i => i.TrackDisplayables.Where(t => refillLocations.Contains(t.GetType())));
|
||||
//Log.Information($"{nameof(OpsController_AnnounceCoalescedPayments_Patch)} => Caboose Helper => PassengerStops => {string.Join(",", passengerStops)}");
|
||||
|
||||
var cabeese = passengerStops
|
||||
.SelectMany(t => t.TrackSpans?.Select(s => (tc.CarsOnSpan(s) ?? []).Where(c => c.IsCaboose()))?.SelectMany(c => c?.Select(c2 => (t, c2))));
|
||||
//Log.Information($"{nameof(OpsController_AnnounceCoalescedPayments_Patch)} => Caboose Helper => PassengerStops Cabeese => {string.Join(",", cabeese?.Select(c => $"{c.t} : {c.c2}") ?? [])}");
|
||||
|
||||
CrewCarDict = CrewCarDict.Where(kvp => cabeese.Select(c => c.c2.id).Contains(kvp.Key)).ToDictionary(k => k.Key, v => v.Value);
|
||||
|
||||
var deltaTime = (float)(TimeWeather.Now.TotalSeconds - dateTime.TotalSeconds);
|
||||
foreach (var caboose in cabeese)
|
||||
{
|
||||
//Log.Information($"{nameof(OpsController_AnnounceCoalescedPayments_Patch)} => Caboose Helper ({deltaTime}) => {caboose.t} : {caboose.c2}");
|
||||
CarLoadCrewHelper(caboose.c2, deltaTime);
|
||||
}
|
||||
dateTime = TimeWeather.Now;
|
||||
} catch (System.Exception ex)
|
||||
{
|
||||
Log.Error(ex, "error with announce caboose helper");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using JetBrains.Annotations;
|
||||
using Model;
|
||||
using Model.AI;
|
||||
using Model.OpsNew;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Track;
|
||||
using TweaksAndThings;
|
||||
using UI;
|
||||
using UI.Builder;
|
||||
using UI.CarInspector;
|
||||
using UI.Tags;
|
||||
using UnityEngine;
|
||||
using static Model.Car;
|
||||
using tat = TweaksAndThings.TweaksAndThings;
|
||||
|
||||
namespace TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(TagController))]
|
||||
[HarmonyPatch(nameof(TagController.UpdateTag), typeof(Car), typeof(TagCallout), typeof(OpsController))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
public class TagConroller_UpdateTag_Patch
|
||||
{
|
||||
private static void Postfix(Car car, TagCallout tagCallout)
|
||||
{
|
||||
TagController tagController = UnityEngine.Object.FindObjectOfType<TagController>();
|
||||
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.Shared;
|
||||
tagCallout.callout.Title = $"<align=left>{car.DisplayName}<line-height=0>";
|
||||
|
||||
if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tagCallout.gameObject.SetActive(
|
||||
tagCallout.gameObject.activeSelf &&
|
||||
(!GameInput.IsShiftDown || (GameInput.IsShiftDown && car.CarOrEndGearIssue()))
|
||||
);
|
||||
if (tagCallout.gameObject.activeSelf && GameInput.IsShiftDown && car.CarOrEndGearIssue()) {
|
||||
|
||||
tagController.ApplyImageColor(tagCallout, Color.black);
|
||||
}
|
||||
|
||||
if (car.CarAndEndGearIssue())
|
||||
{
|
||||
tagCallout.callout.Title =
|
||||
$"{tagCallout.callout.Title}\n<align=\"right\">{TextSprites.CycleWaybills}{TextSprites.HandbrakeWheel}";
|
||||
}
|
||||
else if (car.EndAirSystemIssue())
|
||||
tagCallout.callout.Title =
|
||||
$"{tagCallout.callout.Title}\n<align=\"right\">{TextSprites.CycleWaybills}";
|
||||
else if (car.HandbrakeApplied())
|
||||
tagCallout.callout.Title =
|
||||
$"{tagCallout.callout.Title}\n<align=\"right\">{TextSprites.HandbrakeWheel}";
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModelCarExtensions
|
||||
{
|
||||
public static bool EndAirSystemIssue(this Model.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();
|
||||
}
|
||||
51
TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs
Normal file
51
TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using HarmonyLib;
|
||||
using Model;
|
||||
using Model.OpsNew;
|
||||
using Railloader;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UI.Tags;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
[HarmonyPatch(typeof(TagController))]
|
||||
[HarmonyPatch(nameof(TagController.UpdateTag), typeof(Car), typeof(TagCallout), typeof(OpsController))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class TagController_UpdateTag_Patch
|
||||
{
|
||||
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 static void Postfix(Car car, TagCallout tagCallout)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
|
||||
if (!tweaksAndThings.IsEnabled || !tweaksAndThings.settings.HandBrakeAndAirTagModifiers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProceedWithPostFix(car, tagCallout);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static void ProceedWithPostFix(Car car, TagCallout tagCallout)
|
||||
{
|
||||
tagCallout.callout.Title = string.Format(tagTitleFormat, "{0}", car.DisplayName);
|
||||
List<string> tags = [];
|
||||
|
||||
if (OpsController_AnnounceCoalescedPayments_Patch.CrewCarStatus(car).spotted) tags.Add("+");
|
||||
if (car.HasHotbox) tags.Add(TextSprites.Hotbox);
|
||||
if (car.EndAirSystemIssue()) tags.Add(TextSprites.CycleWaybills);
|
||||
if (car.HandbrakeApplied()) tags.Add(TextSprites.HandbrakeWheel);
|
||||
|
||||
tagCallout.callout.Title =
|
||||
tags.Any() switch
|
||||
{
|
||||
true => $"{tagCallout.callout.Title}{tagTitleAndIconDelimeter}{string.Join("", tags)}".Replace("{0}", tags.Count().ToString()),
|
||||
_ => car.DisplayName
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,14 @@
|
||||
<!-- <MajorVersion>1</MajorVersion> -->
|
||||
<!-- <MinorVersion>0</MinorVersion> -->
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="mroc-cabeese.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="mroc-cabeese.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<GameAssembly Include="Assembly-CSharp" />
|
||||
<GameAssembly Include="Railloader.Interchange" />
|
||||
@@ -11,10 +19,15 @@
|
||||
<GameAssembly Include="0Harmony" />
|
||||
<GameAssembly Include="KeyValue.Runtime" />
|
||||
<GameAssembly Include="Definition" />
|
||||
<GameAssembly Include="Ops" />
|
||||
<GameAssembly Include="StrangeCustoms" />
|
||||
|
||||
<GameAssembly Include="UnityEngine.CoreModule" />
|
||||
<GameAssembly Include="UnityEngine.UI" />
|
||||
<GameAssembly Include="Unity.TextMeshPro" />
|
||||
|
||||
<GameAssembly Include="System.Net.Http" />
|
||||
<GameAssembly Include="Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.2.1">
|
||||
@@ -27,10 +40,4 @@
|
||||
<ItemGroup>
|
||||
<Publicize Include="Assembly-CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="ForYourConvenience">
|
||||
<HintPath>D:\SteamLibrary\steamapps\common\Railroader\Mods\ForYourConvenience\ForYourConvenience.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Runtime;
|
||||
using System;
|
||||
using Serilog;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TweaksAndThings;
|
||||
|
||||
public class Settings
|
||||
{
|
||||
|
||||
public Settings()
|
||||
{}
|
||||
|
||||
public Settings(
|
||||
WebhookSettings webhookSettings,
|
||||
List<WebhookSettings> webhookSettingsList,
|
||||
bool handBrakeAndAirTagModifiers
|
||||
)
|
||||
{
|
||||
this.WebhookSettingsList = webhookSettingsList;
|
||||
this.HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers;
|
||||
}
|
||||
|
||||
public List<WebhookSettings> WebhookSettingsList;
|
||||
public bool HandBrakeAndAirTagModifiers;
|
||||
|
||||
internal void AddAnotherRow()
|
||||
{
|
||||
//if (!string.IsNullOrEmpty(WebhookSettingsList.Last().WebhookUrl)) WebhookSettingsList.Add(new());
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookSettings
|
||||
{
|
||||
public WebhookSettings() { }
|
||||
public WebhookSettings(
|
||||
bool webhookEnabled,
|
||||
string railroadMark,
|
||||
string webhookUrl
|
||||
)
|
||||
{
|
||||
this.WebhookEnabled = webhookEnabled;
|
||||
this.RailroadMark = railroadMark;
|
||||
this.WebhookUrl = webhookUrl;
|
||||
}
|
||||
|
||||
public bool WebhookEnabled = false;
|
||||
public string RailroadMark = string.Empty;
|
||||
public string WebhookUrl = string.Empty;
|
||||
}
|
||||
113
TweaksAndThings/Settings/Settings.cs
Normal file
113
TweaksAndThings/Settings/Settings.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using Serilog;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RMROC451.TweaksAndThings.Enums;
|
||||
using UI.Builder;
|
||||
using Model;
|
||||
using RMROC451.TweaksAndThings.Extensions;
|
||||
|
||||
namespace RMROC451.TweaksAndThings;
|
||||
|
||||
public class Settings
|
||||
{
|
||||
|
||||
public Settings()
|
||||
{
|
||||
WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
|
||||
EngineRosterFuelColumnSettings = new();
|
||||
}
|
||||
|
||||
public Settings(
|
||||
List<WebhookSettings> webhookSettingsList,
|
||||
bool handBrakeAndAirTagModifiers,
|
||||
RosterFuelColumnSettings engineRosterFuelColumnSettings,
|
||||
bool endGearHelpersRequirePayment,
|
||||
bool requireConsistCabooseForOilerAndHotboxSpotter,
|
||||
bool cabooseAllowsConsistInfo
|
||||
)
|
||||
{
|
||||
WebhookSettingsList = webhookSettingsList;
|
||||
HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers;
|
||||
EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings;
|
||||
EndGearHelpersRequirePayment = endGearHelpersRequirePayment;
|
||||
RequireConsistCabooseForOilerAndHotboxSpotter = requireConsistCabooseForOilerAndHotboxSpotter;
|
||||
CabooseAllowsConsistInfo = cabooseAllowsConsistInfo;
|
||||
}
|
||||
|
||||
public readonly UIState<string> _selectedTabState = new UIState<string>(null);
|
||||
public List<WebhookSettings>? WebhookSettingsList;
|
||||
public bool HandBrakeAndAirTagModifiers;
|
||||
public RosterFuelColumnSettings? EngineRosterFuelColumnSettings;
|
||||
public bool EndGearHelpersRequirePayment;
|
||||
public bool RequireConsistCabooseForOilerAndHotboxSpotter;
|
||||
public bool CabooseAllowsConsistInfo;
|
||||
|
||||
internal void AddAnotherRow()
|
||||
{
|
||||
WebhookSettingsList ??= new[] { new WebhookSettings() }.ToList();
|
||||
if (!string.IsNullOrEmpty(WebhookSettingsList.OrderByDescending(wsl => wsl.WebhookUrl).Last().WebhookUrl))
|
||||
{
|
||||
WebhookSettingsList.Add(new());
|
||||
Log.Information($"Adding another {nameof(WebhookSettings)} list entry, last one was filled in");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookSettings
|
||||
{
|
||||
public WebhookSettings() { }
|
||||
public WebhookSettings(
|
||||
bool webhookEnabled,
|
||||
string railroadMark,
|
||||
string webhookUrl
|
||||
)
|
||||
{
|
||||
WebhookEnabled = webhookEnabled;
|
||||
RailroadMark = railroadMark;
|
||||
WebhookUrl = webhookUrl;
|
||||
}
|
||||
|
||||
public bool WebhookEnabled = false;
|
||||
public string RailroadMark = string.Empty;
|
||||
public string WebhookUrl = string.Empty;
|
||||
}
|
||||
|
||||
public class RosterFuelColumnSettings
|
||||
{
|
||||
public RosterFuelColumnSettings() { }
|
||||
public RosterFuelColumnSettings(
|
||||
bool engineRosterShowsFuelStatusAlways,
|
||||
EngineRosterFuelDisplayColumn engineRosterFuelStatusColumn
|
||||
)
|
||||
{
|
||||
this.EngineRosterShowsFuelStatusAlways = engineRosterShowsFuelStatusAlways;
|
||||
this.EngineRosterFuelStatusColumn = engineRosterFuelStatusColumn;
|
||||
}
|
||||
|
||||
public bool EngineRosterShowsFuelStatusAlways;
|
||||
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;
|
||||
}
|
||||
|
||||
public static bool CabooseAllowsConsistInfo(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.CabooseAllowsConsistInfo ?? false;
|
||||
public static bool EndGearHelpersRequirePayment(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.EndGearHelpersRequirePayment ?? false;
|
||||
public static bool RequireConsistCabooseForOilerAndHotboxSpotter(this TweaksAndThingsPlugin input) =>
|
||||
input?.settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false;
|
||||
public static bool CabooseNonMotiveAllowedSetting(this TweaksAndThingsPlugin input, Car car) =>
|
||||
input.EndGearHelpersRequirePayment() && car.set.Cars.CabooseInConsist() && car.NotMotivePower();
|
||||
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
using GalaSoft.MvvmLight.Messaging;
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
using Serilog.Debugging;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using TweaksAndThings.Commands;
|
||||
using UI.Builder;
|
||||
using UI.CarInspector;
|
||||
|
||||
namespace TweaksAndThings
|
||||
{
|
||||
public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHandler, IModTabHandler
|
||||
{
|
||||
private HttpClient client;
|
||||
internal HttpClient Client
|
||||
{
|
||||
get
|
||||
{
|
||||
if (client == null)
|
||||
client = new HttpClient();
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
internal Settings? settings { get; private set; } = null;
|
||||
readonly ILogger logger = Log.ForContext<TweaksAndThings>();
|
||||
IModdingContext moddingContext { get; set; }
|
||||
IModDefinition modDefinition { get; set; }
|
||||
|
||||
static TweaksAndThings()
|
||||
{
|
||||
Log.Information("Hello! Static Constructor was called!");
|
||||
}
|
||||
|
||||
public TweaksAndThings(IModdingContext moddingContext, IModDefinition self)
|
||||
{
|
||||
this.modDefinition = self;
|
||||
|
||||
this.moddingContext = moddingContext;
|
||||
|
||||
logger.Information("Hello! Constructor was called for {modId}/{modVersion}!", self.Id, self.Version);
|
||||
|
||||
//moddingContext.RegisterConsoleCommand(new EchoCommand());
|
||||
|
||||
settings = moddingContext.LoadSettingsData<Settings>(self.Id);
|
||||
}
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
logger.Information("OnEnable() was called!");
|
||||
var harmony = new Harmony(modDefinition.Id);
|
||||
harmony.PatchCategory(modDefinition.Id.Replace(".",string.Empty));
|
||||
}
|
||||
|
||||
public override void OnDisable()
|
||||
{
|
||||
var harmony = new Harmony(modDefinition.Id);
|
||||
harmony.UnpatchAll(modDefinition.Id);
|
||||
Messenger.Default.Unregister(this);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
logger.Verbose("UPDATE()");
|
||||
}
|
||||
|
||||
public void ModTabDidOpen(UIPanelBuilder builder)
|
||||
{
|
||||
logger.Information("Daytime!");
|
||||
//WebhookUISection(ref builder);
|
||||
//builder.AddExpandingVerticalSpacer();
|
||||
WebhooksListUISection(ref builder);
|
||||
builder.AddExpandingVerticalSpacer();
|
||||
HandbrakesAndAnglecocksUISection(ref builder);
|
||||
}
|
||||
|
||||
private void HandbrakesAndAnglecocksUISection(ref UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddSection("Tag Callout Handbrake and Air System Helper", delegate (UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddField(
|
||||
"Enable Tag Updates",
|
||||
builder.AddToggle(
|
||||
() => settings?.HandBrakeAndAirTagModifiers ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
||||
settings.HandBrakeAndAirTagModifiers = enabled;
|
||||
builder.Rebuild();
|
||||
}
|
||||
)
|
||||
).Tooltip("Enable Tag Updates", $"Will add {TextSprites.CycleWaybills} to the car tag title having Air System issues. Also prepends {TextSprites.HandbrakeWheel} if there is a handbrake set.\n\nHolding Shift while tags are displayed only shows tag titles that have issues.");
|
||||
});
|
||||
}
|
||||
|
||||
private void WebhooksListUISection(ref UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddSection("Webhooks List", delegate (UIPanelBuilder builder)
|
||||
{
|
||||
for (int i = 1; i <= settings.WebhookSettingsList.Count; i++)
|
||||
{
|
||||
int z = i - 1;
|
||||
builder.AddSection($"Webhook {i}", delegate (UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddField(
|
||||
"Webhook Enabled",
|
||||
builder.AddToggle(
|
||||
() => settings?.WebhookSettingsList[z]?.WebhookEnabled ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
||||
settings.WebhookSettingsList[z].WebhookEnabled = enabled;
|
||||
settings.AddAnotherRow();
|
||||
builder.Rebuild();
|
||||
}
|
||||
)
|
||||
).Tooltip("Webhook Enabled", "Will parse the console messages and transmit to a Discord webhook.");
|
||||
|
||||
builder.AddField(
|
||||
"Reporting Mark",
|
||||
builder.HStack(delegate (UIPanelBuilder field)
|
||||
{
|
||||
field.AddInputField(
|
||||
settings?.WebhookSettingsList[z]?.RailroadMark,
|
||||
delegate (string railroadMark)
|
||||
{
|
||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
||||
settings.WebhookSettingsList[z].RailroadMark = railroadMark;
|
||||
settings.AddAnotherRow();
|
||||
builder.Rebuild();
|
||||
}, characterLimit: GameStorage.ReportingMarkMaxLength).FlexibleWidth();
|
||||
})
|
||||
).Tooltip("Reporting Mark", "Reporting mark of the company this Discord webhook applies to..");
|
||||
|
||||
builder.AddField(
|
||||
"Webhook Url",
|
||||
builder.HStack(delegate (UIPanelBuilder field)
|
||||
{
|
||||
field.AddInputField(
|
||||
settings?.WebhookSettingsList[z]?.WebhookUrl,
|
||||
delegate (string webhookUrl)
|
||||
{
|
||||
if (settings == null) settings = new() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList() };
|
||||
settings.WebhookSettingsList[z].WebhookUrl = webhookUrl;
|
||||
settings.AddAnotherRow();
|
||||
builder.Rebuild();
|
||||
}).FlexibleWidth();
|
||||
})
|
||||
).Tooltip("Webhook Url", "Url of Discord webhook to publish messages to.");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ModTabDidClose()
|
||||
{
|
||||
logger.Information("Nighttime...");
|
||||
this.moddingContext.SaveSettingsData(this.modDefinition.Id, settings ?? new());
|
||||
}
|
||||
}
|
||||
}
|
||||
252
TweaksAndThings/TweaksAndThingsPlugin.cs
Normal file
252
TweaksAndThings/TweaksAndThingsPlugin.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
// Ignore Spelling: RMROC
|
||||
|
||||
using GalaSoft.MvvmLight.Messaging;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
using Railloader;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using UI.Builder;
|
||||
|
||||
using RMROC451.TweaksAndThings.Enums;
|
||||
using RMROC451.TweaksAndThings.Commands;
|
||||
|
||||
namespace RMROC451.TweaksAndThings;
|
||||
|
||||
public class TweaksAndThingsPlugin : SingletonPluginBase<TweaksAndThingsPlugin>, IUpdateHandler, IModTabHandler
|
||||
{
|
||||
private HttpClient client;
|
||||
internal HttpClient Client
|
||||
{
|
||||
get
|
||||
{
|
||||
if (client == null)
|
||||
client = new HttpClient();
|
||||
|
||||
return client;
|
||||
}
|
||||
}
|
||||
internal Settings? settings { get; private set; } = null;
|
||||
readonly ILogger logger = Log.ForContext<TweaksAndThingsPlugin>();
|
||||
IModdingContext moddingContext { get; set; }
|
||||
IModDefinition modDefinition { get; set; }
|
||||
|
||||
static TweaksAndThingsPlugin()
|
||||
{
|
||||
Log.Information("Hello! Static Constructor was called!");
|
||||
}
|
||||
|
||||
public TweaksAndThingsPlugin(IModdingContext moddingContext, IModDefinition self)
|
||||
{
|
||||
this.modDefinition = self;
|
||||
|
||||
this.moddingContext = moddingContext;
|
||||
|
||||
logger.Information("Hello! Constructor was called for {modId}/{modVersion}!", self.Id, self.Version);
|
||||
|
||||
moddingContext.RegisterConsoleCommand(new EchoCommand());
|
||||
|
||||
settings = moddingContext.LoadSettingsData<Settings>(self.Id);
|
||||
}
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
logger.Information("OnEnable() was called!");
|
||||
var harmony = new Harmony(modDefinition.Id);
|
||||
harmony.PatchCategory(modDefinition.Id.Replace(".", string.Empty));
|
||||
}
|
||||
|
||||
public override void OnDisable()
|
||||
{
|
||||
var harmony = new Harmony(modDefinition.Id);
|
||||
harmony.UnpatchAll(modDefinition.Id);
|
||||
Messenger.Default.Unregister(this);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
logger.Verbose("UPDATE()");
|
||||
}
|
||||
|
||||
public void ModTabDidOpen(UIPanelBuilder builder)
|
||||
{
|
||||
logger.Information("Daytime!");
|
||||
|
||||
if (settings == null) settings = new();
|
||||
if (!settings?.WebhookSettingsList?.Any() ?? true) settings.WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
|
||||
if (settings?.EngineRosterFuelColumnSettings == null) settings.EngineRosterFuelColumnSettings = new();
|
||||
|
||||
|
||||
settings.WebhookSettingsList =
|
||||
settings?.WebhookSettingsList.SanitizeEmptySettings();
|
||||
|
||||
builder.AddTabbedPanels(settings._selectedTabState, delegate (UITabbedPanelBuilder tabBuilder)
|
||||
{
|
||||
tabBuilder.AddTab("Caboose Mods", "cabooseUpdates", CabooseMods);
|
||||
tabBuilder.AddTab("UI", "rosterUi", UiUpdates);
|
||||
tabBuilder.AddTab("Webhooks", "webhooks", WebhooksListUISection);
|
||||
});
|
||||
}
|
||||
|
||||
private static string cabooseUse => "Caboose Use";
|
||||
private static string autoAiRequirment => "AutoAI\nRequirement";
|
||||
|
||||
private void CabooseMods(UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddField(
|
||||
cabooseUse,
|
||||
builder.AddToggle(
|
||||
() => settings?.EndGearHelpersRequirePayment ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.EndGearHelpersRequirePayment = enabled;
|
||||
builder.Rebuild();
|
||||
}
|
||||
)
|
||||
).Tooltip("Enable End Gear Helper Cost", @$"Will cost 1 minute of AI Brake Crew & Caboose Crew time per car in the consist when the new inspector buttons are utilized.
|
||||
|
||||
1.5x multiplier penalty to AI Brake Crew cost if no sufficiently crewed caboose nearby.
|
||||
|
||||
Caboose starts reloading `Crew Hours` at any Team or Repair track (no waybill), after being stationary for 30 seconds.
|
||||
|
||||
AutoOiler Update: Increases limit that crew will oiling a car from 75% -> 99%, also halves the time it takes (simulating crew from lead end and caboose handling half the train).
|
||||
|
||||
AutoOiler Update: if `{cabooseUse}` & `{autoAiRequirment.Replace("\n", " ")}` checked, then when a caboose is present, the AutoOiler will repair hotboxes afer oiling them to 100%.
|
||||
|
||||
AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - 30 seconds (Safety Is Everyone's Job)");
|
||||
|
||||
builder.AddField(
|
||||
autoAiRequirment,
|
||||
builder.AddToggle(
|
||||
() => settings?.RequireConsistCabooseForOilerAndHotboxSpotter ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.RequireConsistCabooseForOilerAndHotboxSpotter = enabled;
|
||||
builder.Rebuild();
|
||||
}
|
||||
)
|
||||
).Tooltip("AI Engineer Requires Caboose", $@"A caboose is required in the consist to check for Hotboxes and perform Auto Oiler, if checked.");
|
||||
}
|
||||
|
||||
private void UiUpdates(UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddField(
|
||||
"Enable Tag Updates",
|
||||
builder.AddToggle(
|
||||
() => settings?.HandBrakeAndAirTagModifiers ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.HandBrakeAndAirTagModifiers = enabled;
|
||||
builder.Rebuild();
|
||||
}
|
||||
)
|
||||
).Tooltip("Enable Tag Updates", $@"Will suffix tag title with:
|
||||
{TextSprites.CycleWaybills} if Air System issue.
|
||||
{TextSprites.HandbrakeWheel} if there is a handbrake set.
|
||||
{TextSprites.Hotbox} if a hotbox.");
|
||||
|
||||
EngineRosterShowsFuelStatusUISection(builder);
|
||||
}
|
||||
|
||||
private void EngineRosterShowsFuelStatusUISection(UIPanelBuilder builder)
|
||||
{
|
||||
var columns = Enum.GetValues(typeof(EngineRosterFuelDisplayColumn)).Cast<EngineRosterFuelDisplayColumn>().Select(i => i.ToString()).ToList();
|
||||
builder.AddSection("Fuel Display in Engine Roster", delegate (UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddField(
|
||||
"Enable",
|
||||
builder.AddDropdown(columns, (int)(settings?.EngineRosterFuelColumnSettings?.EngineRosterFuelStatusColumn ?? EngineRosterFuelDisplayColumn.None),
|
||||
delegate (int column)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.EngineRosterFuelColumnSettings.EngineRosterFuelStatusColumn = (EngineRosterFuelDisplayColumn)column;
|
||||
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)))}");
|
||||
|
||||
builder.AddField(
|
||||
"Always Visible?",
|
||||
builder.AddToggle(
|
||||
() => settings?.EngineRosterFuelColumnSettings?.EngineRosterShowsFuelStatusAlways ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.EngineRosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways = enabled;
|
||||
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.");
|
||||
});
|
||||
}
|
||||
|
||||
private void WebhooksListUISection(UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddSection("Webhooks List", delegate (UIPanelBuilder builder)
|
||||
{
|
||||
for (int i = 1; i <= settings.WebhookSettingsList.Count; i++)
|
||||
{
|
||||
int z = i - 1;
|
||||
builder.AddSection($"Webhook {i}", delegate (UIPanelBuilder builder)
|
||||
{
|
||||
builder.AddField(
|
||||
"Webhook Enabled",
|
||||
builder.AddToggle(
|
||||
() => settings?.WebhookSettingsList[z]?.WebhookEnabled ?? false,
|
||||
delegate (bool enabled)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.WebhookSettingsList[z].WebhookEnabled = enabled;
|
||||
settings.AddAnotherRow();
|
||||
builder.Rebuild();
|
||||
}
|
||||
)
|
||||
).Tooltip("Webhook Enabled", "Will parse the console messages and transmit to a Discord webhook.");
|
||||
|
||||
builder.AddField(
|
||||
"Reporting Mark",
|
||||
builder.HStack(delegate (UIPanelBuilder field)
|
||||
{
|
||||
field.AddInputField(
|
||||
settings?.WebhookSettingsList[z]?.RailroadMark,
|
||||
delegate (string railroadMark)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.WebhookSettingsList[z].RailroadMark = railroadMark;
|
||||
settings.AddAnotherRow();
|
||||
builder.Rebuild();
|
||||
}, characterLimit: GameStorage.ReportingMarkMaxLength).FlexibleWidth();
|
||||
})
|
||||
).Tooltip("Reporting Mark", "Reporting mark of the company this Discord webhook applies to..");
|
||||
|
||||
builder.AddField(
|
||||
"Webhook Url",
|
||||
builder.HStack(delegate (UIPanelBuilder field)
|
||||
{
|
||||
field.AddInputField(
|
||||
settings?.WebhookSettingsList[z]?.WebhookUrl,
|
||||
delegate (string webhookUrl)
|
||||
{
|
||||
if (settings == null) settings = new();
|
||||
settings.WebhookSettingsList[z].WebhookUrl = webhookUrl;
|
||||
settings.AddAnotherRow();
|
||||
builder.Rebuild();
|
||||
}).FlexibleWidth();
|
||||
})
|
||||
).Tooltip("Webhook Url", "Url of Discord webhook to publish messages to.");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void ModTabDidClose()
|
||||
{
|
||||
logger.Information("Nighttime...");
|
||||
this.moddingContext.SaveSettingsData(this.modDefinition.Id, settings ?? new());
|
||||
}
|
||||
}
|
||||
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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
12
updates.json
Normal file
12
updates.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"RMROC451.TweaksAndThings": {
|
||||
"version": "1.0.0",
|
||||
"changelog": [
|
||||
{
|
||||
"version": "*",
|
||||
"date": "2024-07-26T14:31:49.0948925Z",
|
||||
"desc": "https://github.com/rmroc451/TweaksAndThings/releases"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user