Merge pull request #16 from rmroc451/12-add-fuel-fill-level-sprite-to-engine-roster

12 add fuel fill level sprite to engine roster & 14 fix locomotive set hand brake in consist.
This commit is contained in:
2024-06-20 22:40:16 -05:00
committed by GitHub
13 changed files with 441 additions and 245 deletions

View File

@@ -12,7 +12,7 @@
<PropertyGroup Condition="'$(AssemblyVersion)' == '' OR '$(MajorVersion)' != '' OR '$(MinorVersion)' != ''">
<MajorVersion Condition="'$(MajorVersion)' == ''">0</MajorVersion>
<MinorVersion Condition="'$(MinorVersion)' == ''">1</MinorVersion>
<PatchVersion Condition="'$(PatchVersion)' == ''">4</PatchVersion>
<PatchVersion Condition="'$(PatchVersion)' == ''">5</PatchVersion>
<AssemblyVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<ProductVersion>$(AssemblyVersion)</ProductVersion>

View File

@@ -20,7 +20,7 @@ This mod requires Railloader by Zamu.
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`

View File

@@ -0,0 +1,9 @@
namespace RMROC451.TweaksAndThings.Enums;
public enum EngineRosterFuelDisplayColumn
{
None,
Engine,
Crew,
Status
}

View File

@@ -1,4 +1,4 @@
namespace TweaksAndThings.Enums;
namespace RMROC451.TweaksAndThings.Enums;
public enum MrocHelperType
{

View File

@@ -0,0 +1,40 @@
using Model;
using System;
namespace RMROC451.TweaksAndThings.Extensions
{
public static class Car_Extensions
{
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)
{
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;
}
}
}

View File

@@ -3,16 +3,17 @@ using Game.State;
using HarmonyLib;
using KeyValue.Runtime;
using Railloader;
using RMROC451.TweaksAndThings.Enums;
using RMROC451.TweaksAndThings.Extensions;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using TweaksAndThings.Enums;
using UI.Builder;
using UI.CarInspector;
using static Model.Car;
namespace TweaksAndThings.Patches;
namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatch(typeof(CarInspector))]
[HarmonyPatch(nameof(CarInspector.PopulateCarPanel), typeof(UIPanelBuilder))]
@@ -90,6 +91,16 @@ public class CarInspector_PopulateCarPanel_Patch
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)
{
IEnumerable<Model.Car> consist = car.EnumerateCoupled(LogicalEnd.A);
@@ -104,6 +115,9 @@ public class CarInspector_PopulateCarPanel_Patch
} else
{
TrainController tc = UnityEngine.Object.FindObjectOfType<TrainController>();
consist = consist.Where(c => c.DetermineFuelCar(true) != null);
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.
tc.ApplyHandbrakesAsNeeded(consist.ToList(), PlaceTrainHandbrakes.Automatic);
}
break;

View 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")]
public class EngineRosterRow_Refresh_Patch
{
public static void Postfix(EngineRosterRow __instance)
{
TweaksAndThings? tweaksAndThings = SingletonPluginBase<TweaksAndThings>.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}%";
}
}

View File

@@ -14,7 +14,7 @@ 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))]
@@ -35,9 +35,7 @@ public class ExpandedConsole_Add_Patch
TweaksAndThings tweaksAndThings = SingletonPluginBase<TweaksAndThings>.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");

View File

@@ -2,11 +2,12 @@
using Model;
using Model.OpsNew;
using Railloader;
using RMROC451.TweaksAndThings.Extensions;
using UI;
using UI.Tags;
using UnityEngine;
namespace TweaksAndThings.Patches;
namespace RMROC451.TweaksAndThings.Patches;
[HarmonyPatch(typeof(TagController))]
[HarmonyPatch(nameof(TagController.UpdateTag), typeof(Car), typeof(TagCallout), typeof(OpsController))]
@@ -55,23 +56,3 @@ public class TagController_UpdateTag_Patch
};
}
}
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();
}

View File

@@ -11,6 +11,7 @@
<GameAssembly Include="0Harmony" />
<GameAssembly Include="KeyValue.Runtime" />
<GameAssembly Include="Definition" />
<GameAssembly Include="Ops" />
<GameAssembly Include="UnityEngine.CoreModule" />
<GameAssembly Include="UnityEngine.UI" />

View File

@@ -1,58 +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()
{
WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
}
public Settings(
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());
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
)
{
this.WebhookEnabled = webhookEnabled;
this.RailroadMark = railroadMark;
this.WebhookUrl = webhookUrl;
}
public bool WebhookEnabled = false;
public string RailroadMark = string.Empty;
public string WebhookUrl = string.Empty;
}

View File

@@ -0,0 +1,76 @@
using Serilog;
using System.Collections.Generic;
using System.Linq;
using RMROC451.TweaksAndThings.Enums;
namespace RMROC451.TweaksAndThings;
public class Settings
{
public Settings()
{
WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
EngineRosterFuelColumnSettings = new();
}
public Settings(
List<WebhookSettings> webhookSettingsList,
bool handBrakeAndAirTagModifiers,
RosterFuelColumnSettings engineRosterFuelColumnSettings
)
{
WebhookSettingsList = webhookSettingsList;
HandBrakeAndAirTagModifiers = handBrakeAndAirTagModifiers;
EngineRosterFuelColumnSettings = engineRosterFuelColumnSettings;
}
public List<WebhookSettings>? WebhookSettingsList;
public bool HandBrakeAndAirTagModifiers;
public RosterFuelColumnSettings? EngineRosterFuelColumnSettings;
internal void AddAnotherRow()
{
WebhookSettingsList = !WebhookSettingsList?.Any() ?? false ? new[] { new WebhookSettings() }.ToList() : new List<WebhookSettings>();
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;
}

View File

@@ -1,14 +1,19 @@
using GalaSoft.MvvmLight.Messaging;
// 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;
namespace TweaksAndThings
{
using RMROC451.TweaksAndThings.Enums;
namespace RMROC451.TweaksAndThings;
public class TweaksAndThings : SingletonPluginBase<TweaksAndThings>, IUpdateHandler, IModTabHandler
{
private HttpClient client;
@@ -43,12 +48,6 @@ namespace TweaksAndThings
//moddingContext.RegisterConsoleCommand(new EchoCommand());
settings = moddingContext.LoadSettingsData<Settings>(self.Id);
if (!settings?.WebhookSettingsList?.Any() ?? true)
{
if (settings == null) settings = new();
settings.WebhookSettingsList = new[] { new WebhookSettings() }.ToList();
this.moddingContext.SaveSettingsData(this.modDefinition.Id, settings ?? new());
}
}
public override void OnEnable()
@@ -73,11 +72,50 @@ namespace TweaksAndThings
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();
//WebhookUISection(ref builder);
//builder.AddExpandingVerticalSpacer();
WebhooksListUISection(ref builder);
builder.AddExpandingVerticalSpacer();
HandbrakesAndAnglecocksUISection(ref builder);
builder.AddExpandingVerticalSpacer();
EnginRosterShowsFuelStatusUISection(ref builder);
}
private void EnginRosterShowsFuelStatusUISection(ref 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() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList(), EngineRosterFuelColumnSettings = 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() { WebhookSettingsList = new[] { new WebhookSettings() }.ToList(), EngineRosterFuelColumnSettings = 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 HandbrakesAndAnglecocksUISection(ref UIPanelBuilder builder)
@@ -164,4 +202,3 @@ namespace TweaksAndThings
this.moddingContext.SaveSettingsData(this.modDefinition.Id, settings ?? new());
}
}
}