mirror of
https://github.com/rmroc451/TweaksAndThings.git
synced 2025-12-16 01:09:38 -06:00
using "area" cabeese search rather than iterating nearby cars in a catchment area. Adding MU auto filter for engine roster and including other loco fuel levels in single engine when in same consist/mu'd.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using Model.AI;
|
||||
using Game.Messages;
|
||||
using Model.AI;
|
||||
using Network;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
@@ -61,7 +63,8 @@ namespace RMROC451.TweaksAndThings.Extensions
|
||||
if (car.HasHotbox && car.Oiled == 1f && cabooseRequired && foundCaboose)
|
||||
{
|
||||
_log.Information("AutoOiler {name}: {foundCaboose} repaired hotbox {car}", oiler.name, foundCaboose, car);
|
||||
car.AdjustHotboxValue();
|
||||
Multiplayer.Broadcast($"{Hyperlink.To(oiler._originCar)}: \"{Hyperlink.To(car)} hotbox repaired!\"");
|
||||
car.SendPropertyChange(PropertyChange.Control.Hotbox, false);
|
||||
}
|
||||
num += adjustedTimeToWalk;
|
||||
oiler._pendingRunDuration += adjustedTimeToWalk;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using Helpers;
|
||||
using Model;
|
||||
using Model.Definition;
|
||||
@@ -64,6 +65,8 @@ public static class Car_Extensions
|
||||
|
||||
public static bool IsCaboose(this Car car) => car.Archetype == Model.Definition.CarArchetype.Caboose;
|
||||
|
||||
public static Car? CarCaboose(this Car car) => car.IsCaboose() ? car : null;
|
||||
|
||||
public static bool IsCabooseAndStoppedForLoadRefresh(this Car car, bool isFull) => car.IsCaboose() && car.IsStopped(30f) && !isFull;
|
||||
|
||||
public static Car? CabooseInConsist(this IEnumerable<Car> input) => input.FirstOrDefault(IsCaboose);
|
||||
@@ -96,12 +99,14 @@ public static class Car_Extensions
|
||||
t.TrainClass == Timetable.TrainClass.First;
|
||||
|
||||
public static Car? FindMyCaboose(this Car car, float timeNeeded, bool decrement = false) =>
|
||||
car.CarsNearCurrentCar(timeNeeded, decrement).FindNearestCabooseFromNearbyCars();
|
||||
(
|
||||
car.CarCaboose() ?? car.CarsNearCurrentCar(timeNeeded, decrement).FindNearestCabooseFromNearbyCars()
|
||||
)?.CabooseWithSufficientCrewHours(timeNeeded, decrement);
|
||||
|
||||
public static Car? CabooseWithSufficientCrewHours(this Car car, float timeNeeded, bool decrement = false)
|
||||
{
|
||||
Car? output = null;
|
||||
if (!car.IsCaboose()) return null;
|
||||
if (car is null || !car.IsCaboose()) return null;
|
||||
|
||||
List<LoadSlot> loadSlots = car.Definition.LoadSlots;
|
||||
for (int i = 0; i < loadSlots.Count; i++)
|
||||
@@ -118,29 +123,24 @@ public static class Car_Extensions
|
||||
return output;
|
||||
}
|
||||
|
||||
private static Car FindNearestCabooseFromNearbyCars(this List<(Car car, bool crewCar, float distance)> source) =>
|
||||
private static Car? FindNearestCabooseFromNearbyCars(this IEnumerable<(Car car, bool crewCar, float distance)> source) =>
|
||||
source
|
||||
.OrderBy(c => c.crewCar ? 0 : 1)
|
||||
.ThenBy(c => c.distance)
|
||||
.Select(c => c.car)
|
||||
.FirstOrDefault();
|
||||
?.OrderBy(c => c.crewCar ? 0 : 1)
|
||||
?.ThenBy(c => c.distance)
|
||||
?.Select(c => c.car)
|
||||
?.FirstOrDefault();
|
||||
|
||||
private static List<(Car car, bool crewCar, float distance)> CarsNearCurrentCar(this Car car, float timeNeeded, bool decrement)
|
||||
private static IEnumerable<(Car car, bool crewCar, float distance)> CarsNearCurrentCar(this Car car, float timeNeeded, bool decrement)
|
||||
{
|
||||
Area carArea = OpsController.Shared.ClosestArea(car);
|
||||
|
||||
var cabeese =
|
||||
car.EnumerateCoupled().SelectMany(consistCar =>
|
||||
{
|
||||
Vector3 position = consistCar.GetMotionSnapshot().Position;
|
||||
Vector3 center = WorldTransformer.WorldToGame(position);
|
||||
var o = TrainController.Shared
|
||||
.CarIdsInRadius(center, SingletonPluginBase<TweaksAndThingsPlugin>.Shared.CabeeseSearchRadiusInMeters())
|
||||
.Where(c => TrainController.Shared.CarForId(c).IsCaboose());
|
||||
return o;
|
||||
}).Distinct().Select(c => TrainController.Shared.CarForId(c));
|
||||
var cabeese = OpsController.Shared
|
||||
.CarsInArea(carArea)
|
||||
.Select(c => TrainController.Shared.CarForId(c.Id))
|
||||
.Union(car.EnumerateCoupled())
|
||||
.Where(c => c.IsCaboose());
|
||||
|
||||
|
||||
Log.Information($"{nameof(CarsNearCurrentCar)} => {cabeese.Count()}");
|
||||
//if (cabeese?.Any() ?? false) Log.Information($"{nameof(CarsNearCurrentCar)}[{car.DisplayName}] => {cabeese.Count()}");
|
||||
|
||||
List<(Car car, bool crewCar, float distance)> source =
|
||||
cabeese.Select(c => (car: c, crewCar: c.IsCrewCar(), distance: car.Distance(c))).ToList();
|
||||
@@ -158,9 +158,16 @@ public static class Car_Extensions
|
||||
}
|
||||
|
||||
public static bool IsCrewCar(this Car car) =>
|
||||
!string.IsNullOrEmpty(TrainController.Shared.SelectedLocomotive.trainCrewId) &&
|
||||
car.trainCrewId == TrainController.Shared.SelectedLocomotive.trainCrewId;
|
||||
!string.IsNullOrEmpty(TrainController.Shared.SelectedLocomotive?.trainCrewId) &&
|
||||
car.trainCrewId == TrainController.Shared.SelectedLocomotive?.trainCrewId;
|
||||
|
||||
|
||||
public static void AdjustHotboxValue(this Car car) => car.ControlProperties[PropertyChange.Control.Hotbox] = null;
|
||||
//public static void AdjustHotboxValue(this Car car) => car.ControlProperties[PropertyChange.Control.Hotbox] = null;
|
||||
public static void AdjustHotboxValue(this Car car, float hotboxValue = 0f) =>
|
||||
StateManager.ApplyLocal(
|
||||
new PropertyChange(
|
||||
car.id, PropertyChange.KeyForControl(PropertyChange.Control.Hotbox),
|
||||
new FloatPropertyValue(hotboxValue)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
31
TweaksAndThings/Patches/EngineRosterPanel_Populate_Patch.cs
Normal file
31
TweaksAndThings/Patches/EngineRosterPanel_Populate_Patch.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using HarmonyLib;
|
||||
using Railloader;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UI;
|
||||
using UI.EngineRoster;
|
||||
|
||||
namespace RMROC451.TweaksAndThings.Patches;
|
||||
|
||||
|
||||
[HarmonyPatch(typeof(EngineRosterPanel))]
|
||||
[HarmonyPatch(nameof(EngineRosterPanel.Populate))]
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class EngineRosterPanel_Populate_Patch
|
||||
{
|
||||
private static bool Prefix(EngineRosterPanel __instance, ref List<RosterRowData> rows)
|
||||
{
|
||||
TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
|
||||
__instance._window.Title = __instance._window.Title.Split(':')[0].Trim();
|
||||
if (!tweaksAndThings.IsEnabled()) return true;
|
||||
|
||||
var hiddenEntries = rows.Where(r => r.Engine.IsMuEnabled && !r.IsSelected && !r.IsFavorite).Select(r => r.Engine.id) ?? Enumerable.Empty<string>();
|
||||
|
||||
if (hiddenEntries.Any()) __instance._window.Title =string.Format("{0} : {1}", __instance._window.Title, $"Hidden MU Count [{hiddenEntries.Count()}]");
|
||||
|
||||
rows = rows.Where(r => !hiddenEntries.Contains(r.Engine.id)).ToList();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ namespace RMROC451.TweaksAndThings.Patches;
|
||||
[HarmonyPatchCategory("RMROC451TweaksAndThings")]
|
||||
internal class EngineRosterRow_Refresh_Patch
|
||||
{
|
||||
private static Serilog.ILogger _log => Log.ForContext<EngineRosterRow_Refresh_Patch>();
|
||||
|
||||
public static void Postfix(EngineRosterRow __instance)
|
||||
{
|
||||
TweaksAndThingsPlugin? tweaksAndThings = SingletonPluginBase<TweaksAndThingsPlugin>.Shared;
|
||||
@@ -35,46 +37,60 @@ internal class EngineRosterRow_Refresh_Patch
|
||||
if (tweaksAndThings == null ||
|
||||
rosterFuelColumnSettings == null ||
|
||||
!tweaksAndThings.IsEnabled() ||
|
||||
rosterFuelColumnSettings.EngineRosterFuelStatusColumn == EngineRosterFuelDisplayColumn.None || (!GameInput.IsAltDown && !rosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways))
|
||||
rosterFuelColumnSettings.EngineRosterFuelStatusColumn == EngineRosterFuelDisplayColumn.None || (!GameInput.IsAltDown && !rosterFuelColumnSettings.EngineRosterShowsFuelStatusAlways) ||
|
||||
__instance._engine.IsMuEnabled
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IEnumerable<Car> consist = __instance._engine.EnumerateCoupled().Where(c => c.EnableOiling);
|
||||
Car engineOrTender = __instance._engine;
|
||||
IEnumerable<Car> locos = engineOrTender.EnumerateCoupled().Where(c => c.IsLocomotive).ToList();
|
||||
IEnumerable<Car> consist = engineOrTender.EnumerateCoupled().Where(c => c.EnableOiling).ToList();
|
||||
bool cabooseRequirementFulfilled =
|
||||
!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter()
|
||||
|| consist.ConsistNoFreight()
|
||||
|| (bool)__instance._engine.FindMyCaboose(0.0f, false);
|
||||
float offendingPercentage = 0;
|
||||
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();
|
||||
}
|
||||
|| (bool)engineOrTender.FindMyCaboose(0.0f, false);
|
||||
float offendingPercentage = 100f;
|
||||
|
||||
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++)
|
||||
foreach (Car loco in locos)
|
||||
{
|
||||
CarLoadInfo? loadInfo = engineOrTender.GetLoadInfo(i);
|
||||
if (loadInfo.HasValue)
|
||||
var investigate = loco;
|
||||
List<LoadSlot> loadSlots = investigate.Definition.LoadSlots;
|
||||
if (!loadSlots.Any())
|
||||
{
|
||||
CarLoadInfo valueOrDefault = loadInfo.GetValueOrDefault();
|
||||
var fuelLevel = FuelLevel(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity);
|
||||
offendingPercentage = CalcPercentLoad(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";
|
||||
investigate = investigate.DetermineFuelCar()!;
|
||||
loadSlots = investigate != null ? investigate.Definition.LoadSlots : Enumerable.Empty<LoadSlot>().ToList();
|
||||
}
|
||||
|
||||
var offender = loadSlots.OrderBy(ls => (investigate.GetLoadInfo(ls.RequiredLoadIdentifier, out int slotIndex)?.Quantity ?? 0) / loadSlots[slotIndex].MaximumCapacity).FirstOrDefault().RequiredLoadIdentifier;
|
||||
|
||||
|
||||
for (int i = 0; i < loadSlots.Count; i++)
|
||||
{
|
||||
CarLoadInfo? loadInfo = investigate.GetLoadInfo(i);
|
||||
if (loadInfo.HasValue)
|
||||
{
|
||||
CarLoadInfo valueOrDefault = loadInfo.GetValueOrDefault();
|
||||
var fuelLevel = FuelLevel(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity);
|
||||
var offenderCheck = CalcPercentLoad(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity);
|
||||
|
||||
if (offenderCheck < offendingPercentage)
|
||||
{
|
||||
offendingPercentage = offenderCheck;
|
||||
fuelInfoText = loadSlots[i].RequiredLoadIdentifier == offender ? $"{fuelLevel} " : string.Empty;
|
||||
}
|
||||
|
||||
fuelInfoTooltip += $"{TextSprites.PiePercent(valueOrDefault.Quantity, loadSlots[i].MaximumCapacity)} {valueOrDefault.LoadString(CarPrototypeLibrary.instance.LoadForId(valueOrDefault.LoadId))} {(!loco.id.Equals(__instance._engine.id) ? $"{loco.DisplayName}" : "")}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (cabooseRequirementFulfilled && StateManager.Shared.Storage.OilFeature)
|
||||
if (cabooseRequirementFulfilled && StateManager.Shared.Storage.OilFeature && consist.Any())
|
||||
{
|
||||
float lowestOilLevel = consist.OrderBy(c => c.Oiled).FirstOrDefault().Oiled;
|
||||
var oilLevel = FuelLevel(lowestOilLevel, 1);
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Optionally, set a few things to your liking -->
|
||||
<PropertyGroup><!-- Optionally, set a few things to your liking -->
|
||||
<!-- <MajorVersion>1</MajorVersion> -->
|
||||
<!-- <MinorVersion>0</MinorVersion> -->
|
||||
|
||||
<SignAssembly>False</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<Optimize>False</Optimize>
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Patches\BindingsWindow_Patches.cs" />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Ignore Spelling: RMROC
|
||||
|
||||
using GalaSoft.MvvmLight.Messaging;
|
||||
using Game;
|
||||
using Game.Messages;
|
||||
using Game.State;
|
||||
using HarmonyLib;
|
||||
@@ -205,24 +206,6 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 -
|
||||
).Tooltip("Safety First", $@"On non-express timetabled consists, a caboose is required in the consist increase AE max speed > 20 in {Enum.GetName(typeof(AutoEngineerMode), AutoEngineerMode.Road)}/{Enum.GetName(typeof(AutoEngineerMode), AutoEngineerMode.Waypoint)} mode.");
|
||||
#endregion
|
||||
|
||||
#region CabeeseSearchRadius
|
||||
builder.Spacer(spacing);
|
||||
builder.AddField(
|
||||
"Cabeese Search Radius",
|
||||
builder.AddSlider(
|
||||
() => this.CabeeseSearchRadiusInMeters(),
|
||||
() => $"{string.Format(Mathf.CeilToInt(this.CabeeseSearchRadiusInMeters() * 3.28084f).ToString(), "N0")}ft",
|
||||
delegate (float input) {
|
||||
settings = settings ?? new();
|
||||
settings.CabeeseSearchRadiusFtInMeters = Mathf.CeilToInt(input);
|
||||
builder.Rebuild();
|
||||
},
|
||||
minValue: 1f,
|
||||
maxValue: Mathf.CeilToInt(5280f / 2f / 3.28084f),
|
||||
wholeNumbers: true
|
||||
)
|
||||
).Tooltip("Cabeese Catchment Area", "How far should the cabeese hunting logic look away from the cars in the area to find a caboose?");
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void UiUpdates(UIPanelBuilder builder)
|
||||
|
||||
Reference in New Issue
Block a user