diff --git a/Assembly.version b/Assembly.version index 45f4823..a8136ea 100644 --- a/Assembly.version +++ b/Assembly.version @@ -1,7 +1,7 @@ 2 - 0 - 1 + 1 + 0 \ No newline at end of file diff --git a/TweaksAndThings/Commands/CrewUpdateCommand.cs b/TweaksAndThings/Commands/CrewUpdateCommand.cs index a949ac9..f2cffdc 100644 --- a/TweaksAndThings/Commands/CrewUpdateCommand.cs +++ b/TweaksAndThings/Commands/CrewUpdateCommand.cs @@ -46,9 +46,9 @@ public class EchoCommand : IConsoleCommand EntityReference loco = new EntityReference(EntityType.Car, car.id); if (comps[2] == "+") message = new Hyperlink(entityReference.URI(), string.Format(message, OpsController.Shared.ClosestArea(car)?.name ?? "???")); - car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}"); + if (StateManager.IsHost) car.PostNotice(nameof(EchoCommand), $"{message} :{StateManager.Shared._playersManager.LocalPlayer}"); ExpandedConsole_Add_Patch.SendMs(null, $"{Hyperlink.To(car)} {message}"); - Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\""); + if (!StateManager.IsHost) Multiplayer.Broadcast($"{StateManager.Shared._playersManager.LocalPlayer} {Hyperlink.To(car)}: \"{message}\""); return string.Empty; } diff --git a/TweaksAndThings/Definition.json b/TweaksAndThings/Definition.json index a29b876..31fd06d 100644 --- a/TweaksAndThings/Definition.json +++ b/TweaksAndThings/Definition.json @@ -21,6 +21,7 @@ "mixintos": { "container:ne-caboose01": "file(mroc-cabeese.json)", "container:ne-caboose02": "file(mroc-cabeese.json)", - "container:ne-caboose03": "file(mroc-cabeese.json)" + "container:ne-caboose03": "file(mroc-cabeese.json)", + "game-graph": "file(tweakedLoadIDS.json)" } } \ No newline at end of file diff --git a/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs b/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs index a71600c..e96a908 100644 --- a/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs +++ b/TweaksAndThings/Extensions/AutoEngineer_Extensions.cs @@ -30,7 +30,7 @@ namespace RMROC451.TweaksAndThings.Extensions yield break; } oiler._reverse = originIndex > oiler._cars.Count - originIndex; - _log.Information( + _log.Debug( "AutoOiler {name} starting, rev = {reverse}, caboose required = {req}, caboose halving adjustment = {hasCaboose}, oil limit = {limit}", oiler.name, oiler._reverse, @@ -58,11 +58,11 @@ namespace RMROC451.TweaksAndThings.Extensions num += num3; oiler._pendingRunDuration += num3; oiler._oiledCount++; - _log.Information("AutoOiler {name}: oiled {car} from {orig} => {new}", oiler.name, car, origOil, car.Oiled); + _log.Debug("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); + _log.Debug("AutoOiler {name}: {foundCaboose} repaired hotbox {car}", oiler.name, foundCaboose, car); Multiplayer.Broadcast($"{Hyperlink.To(oiler._originCar)}: \"{Hyperlink.To(car)} hotbox repaired!\""); car.SendPropertyChange(PropertyChange.Control.Hotbox, false); } @@ -89,7 +89,7 @@ namespace RMROC451.TweaksAndThings.Extensions continue; } var fc = foundCaboose(); - _log.Information("AutoHotboxSpotter {name}: Hotbox Spotter Running, Found Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}", + _log.Debug("AutoHotboxSpotter {name}: Hotbox Spotter Running, Found Caboose => {hasCaboose}; Has Cars {hasCars}; Requires Caboose {requiresCaboose}", spotter.name, fc, spotter.HasCars, cabooseRequired); if (CabooseRequirementChecker(string.Format("{0} {1}", spotter.GetType().Name, spotter.name), cabooseRequired, fc, _log)) { @@ -104,7 +104,7 @@ namespace RMROC451.TweaksAndThings.Extensions { 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, fc, cabooseRequired); + _log.Debug("AutoHotboxSpotter {name}: Next check went from num(60,300) => {numOrig}; to num(15,30) => {hasCaboose}; Requires Caboose {requiresCaboose}", spotter.name, numOrig, num, fc, cabooseRequired); } yield return new WaitForSeconds(num); spotter.CheckForHotbox(); diff --git a/TweaksAndThings/Extensions/Car_Extensions.cs b/TweaksAndThings/Extensions/Car_Extensions.cs index 18f62c6..67a6c2b 100644 --- a/TweaksAndThings/Extensions/Car_Extensions.cs +++ b/TweaksAndThings/Extensions/Car_Extensions.cs @@ -140,7 +140,7 @@ public static class Car_Extensions .Union(car.EnumerateCoupled()) .Where(c => c.IsCaboose()); - //if (cabeese?.Any() ?? false) Log.Information($"{nameof(CarsNearCurrentCar)}[{car.DisplayName}] => {cabeese.Count()}"); + if (cabeese?.Any() ?? false) Log.Debug($"{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(); diff --git a/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs b/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs index 623a66f..9869789 100644 --- a/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs +++ b/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.cs @@ -38,7 +38,7 @@ internal class AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch if (orig != maxSpeedMph) { - _log.Information($"{Enum.GetName(typeof(AutoEngineerMode), mode)}[{TrainController.Shared.SelectedLocomotive.DisplayName}] {nameof(AutoEngineerOrdersExtensions.MaxSpeedMph)} limited to {limitedSpeed} from {orig}; No Caboose in Consist;"); + _log.Debug($"{Enum.GetName(typeof(AutoEngineerMode), mode)}[{TrainController.Shared.SelectedLocomotive.DisplayName}] {nameof(AutoEngineerOrdersExtensions.MaxSpeedMph)} limited to {limitedSpeed} from {orig}; No Caboose in Consist;"); } } @@ -50,7 +50,9 @@ internal class AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch { var _persistence = new AutoEngineerPersistence(TrainController.Shared.SelectedLocomotive.KeyValueObject); var OrdersHelper = new AutoEngineerOrdersHelper(TrainController.Shared.SelectedLocomotive, _persistence); - + + if (TrainController.Shared.SelectedLocomotive.EnumerateCoupled().All(c => c.IsCaboose() || c.MotivePower())) return false; + bool cabooseReq = SingletonPluginBase.Shared.RequireConsistCabooseForOilerAndHotboxSpotter(); string logMessage = $"\n{nameof(SafetyFirstGoverningApplies)}:{Enum.GetName(typeof(AutoEngineerMode), OrdersHelper.Mode)}[{TrainController.Shared.SelectedLocomotive.DisplayName}] "; Func firstClass = () => @@ -85,7 +87,7 @@ internal class AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch logMessage += $"\nGovern AE? {output}"; - _log.Information(logMessage); + _log.Debug(logMessage); return output; } diff --git a/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SetWaypoint_patch.cs b/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SetWaypoint_patch.cs index a512189..e270df8 100644 --- a/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SetWaypoint_patch.cs +++ b/TweaksAndThings/Patches/AutoEngineerOrdersHelper_SetWaypoint_patch.cs @@ -1,6 +1,8 @@ using Game.Notices; +using Game.State; using HarmonyLib; using Model; +using Network; using Serilog; using UI.EngineControls; using UnityEngine; @@ -16,11 +18,14 @@ internal class AutoEngineerOrdersHelper_SetWaypoint_patch private static Serilog.ILogger _log => Log.ForContext(); static void Postfix(AutoEngineerOrdersHelper __instance, Track.Location location, string coupleToCarId) { - _log.Information($"start setWP"); - Car selectedLoco = __instance._locomotive; - _log.Information($"{selectedLoco?.DisplayName ?? ""} set WP"); - Vector3 gamePoint = location.GetPosition(); - EntityReference entityReference = new EntityReference(EntityType.Position, new Vector4(gamePoint.x, gamePoint.y, gamePoint.z, 0)); - selectedLoco.PostNotice("ai-wpt-rmroc451", new Hyperlink(entityReference.URI(), $"WP SET")); + if (StateManager.IsHost) + { + _log.Debug($"start setWP"); + Car selectedLoco = __instance._locomotive; + _log.Debug($"{selectedLoco?.DisplayName ?? ""} set WP"); + Vector3 gamePoint = location.GetPosition(); + EntityReference entityReference = new EntityReference(EntityType.Position, new Vector4(gamePoint.x, gamePoint.y, gamePoint.z, 0)); + selectedLoco.PostNotice("ai-wpt-rmroc451", new Hyperlink(entityReference.URI(), $"WP SET")); + } } } diff --git a/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs b/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs new file mode 100644 index 0000000..4b8bf7b --- /dev/null +++ b/TweaksAndThings/Patches/AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch.cs @@ -0,0 +1,224 @@ +using Game.State; +using HarmonyLib; +using KeyValue.Runtime; +using Model; +using Model.AI; +using Model.Ops; +using Network; +using Network.Messages; +using Railloader; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using Track; +using Track.Search; +using UI; +using UI.EngineControls; +using UnityEngine; +using static Track.Search.RouteSearch; +using Location = Track.Location; + +namespace RMROC451.TweaksAndThings.Patches; + +[HarmonyPatch(typeof(AutoEngineerWaypointControls))] +[HarmonyPatch(nameof(AutoEngineerWaypointControls.ConfigureOptionsDropdown))] +[HarmonyPatchCategory("RMROC451TweaksAndThings")] +internal class AutoEngineerWaypointControls_ConfigureOptionsDropdown_Patch +{ + private static Serilog.ILogger _log => Log.ForContext(); + + private static readonly HashSet _keyChangeObservers = new HashSet(); + private static readonly HashSet destinations = new HashSet(); + private static readonly HashSet consist = new HashSet(); + private static string _lastLocoCarId = string.Empty; + private static bool recalcing = false; + + static void Postfix(AutoEngineerWaypointControls __instance, ref OptionsDropdownConfiguration __result) + { + + _log.Information($"HI BOB"); + foreach(var o in _keyChangeObservers) o.Dispose(); + _keyChangeObservers.Clear(); + destinations.Clear(); + recalcing = false; + TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; + if (!tweaksAndThings.IsEnabled()) return; + //_log.Information($"HI2"); + + + List rowDatas = __result.Rows.ToList(); + var func = __result.OnRowSelected; + int origCount = rowDatas.Count; + int maxRowOrig = origCount - 1; + + var selectedLoco = TrainController.Shared.SelectedLocomotive; + if (selectedLoco.id != _lastLocoCarId || !(consist?.Any() ?? false)) + { + _lastLocoCarId = selectedLoco.id; + consist.Clear(); + consist.UnionWith(selectedLoco.EnumerateCoupled()?.ToList() ?? Enumerable.Empty()); + } + var aeoh = new AutoEngineerOrdersHelper(persistence: new AutoEngineerPersistence(selectedLoco.KeyValueObject), locomotive: selectedLoco); + List<(string destinationId, string destination, float? distance, Location? location)> jumpTos = new(); + + foreach(var c in consist) + { + AddObserversToCar(__instance, c); + OpsCarPosition? destination = c.Waybill.HasValue && !c.Waybill.Value.Completed ? c.Waybill.Value.Destination : null; + bool completed = c.Waybill?.Completed ?? false; + if (!destination.HasValue && c.TryGetOverrideDestination(OverrideDestination.Repair, OpsController.Shared, out (OpsCarPosition, string)? result)) destination = result.Value.Item1; + _log.Information($"{c.DisplayName} -> {destination.HasValue}"); + + string destId = destination?.Identifier ?? string.Empty; + + if (destinations.Contains(destId)) continue; + + if (destination.HasValue && !completed) + { + string destName = destination.Value.DisplayName; + float? distance = null; + + if (Graph.Shared.TryGetLocationFromPoint(destination.Value.Spans?.FirstOrDefault().GetSegments().FirstOrDefault(), destination.Value.Spans?.FirstOrDefault()?.GetCenterPoint() ?? default, 200f, out Location destLoc)) + { + + float trainMomentum = 0f; + Location start = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.RouteStartLocation(out trainMomentum) : RouteStartLocation(__instance, selectedLoco); + HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer; + List list = new List(); + var totLen = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.CalculateTotalLength() : CalculateTotalLength(selectedLoco); + distance = Graph.Shared.FindRoute(start, destLoc, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum) + ? metrics.Distance + : null; + }; + _log.Information($"{c.DisplayName} -> {destName} {destId} {distance?.ToString()}"); + if (distance.HasValue) + { + destinations.Add(destId); + jumpTos.Add(( + destinationId: destId, + destination: $"WP> {destName}" + , distance: distance + , location: (Location?)destLoc + )); + } + } + } + + jumpTos = jumpTos?.OrderBy(c => c.distance)?.ToList() ?? []; + var safetyFirst = AutoEngineerOrdersHelper_SendAutoEngineerCommand_Patch.SafetyFirstGoverningApplies() && jumpTos.Any(); + + rowDatas.AddRange(jumpTos.Select(j => + new DropdownMenu.RowData( + $"{j.destination} ({(j.distance.HasValue ? Units.DistanceText(j.distance.Value) : "N/A")})", + !safetyFirst ? null : "Disabled; Safety First!" + ) + )); + + __result = new OptionsDropdownConfiguration( + rowDatas + , delegate (int row) + { + _log.Information($"{TrainController.Shared.SelectedLocomotive.DisplayName} row {row}/{jumpTos.Count}/{rowDatas.Count}"); + if (row <= maxRowOrig) + { + func(row); + } + if (row > maxRowOrig && jumpTos[row - origCount].location.HasValue) + { + if (safetyFirst) + { + Multiplayer.SendError(StateManager.Shared.PlayersManager.LocalPlayer, "Safety First, find yourself a caboose!", AlertLevel.Error); + return; + } + float trainMomentum = 0f; + Location end = jumpTos[row - origCount].location.Value; + Location start = RouteStartLocation(__instance, selectedLoco); + HeuristicCosts autoEngineer = HeuristicCosts.AutoEngineer; + List list = new List(); + + var totLen = StateManager.IsHost ? selectedLoco.AutoEngineerPlanner.CalculateTotalLength() : CalculateTotalLength(selectedLoco); + if (!Graph.Shared.FindRoute(start, end, autoEngineer, list, out var metrics, checkForCars: false, totLen, trainMomentum)) + { + RouteSearch.Metrics metrics2; + bool flag = Graph.Shared.FindRoute(start, end, autoEngineer, null, out metrics2); + Multiplayer.SendError(StateManager.Shared.PlayersManager.LocalPlayer, flag ? (selectedLoco.DisplayName + " Train too long to navigate to waypoint.") : (selectedLoco.DisplayName + " Unable to find a path to waypoint."), AlertLevel.Error); + } else + { + var mw = (location: end, carId: string.Empty); + aeoh.SetWaypoint(mw.location, mw.carId); + aeoh.SetOrdersValue(maybeWaypoint: mw); + } + } + } + ); + + } + + private static Location RouteStartLocation(AutoEngineerWaypointControls __instance, BaseLocomotive _locomotive) { + bool num = _locomotive.IsStopped(); + bool? flag = (num ? null : new bool?(_locomotive.velocity >= 0f)); + + bool flag2 = flag ?? __instance.OrdersHelper.Orders.Forward; + if (_locomotive.EndToLogical((!flag2) ? Car.End.R : Car.End.F) == Car.LogicalEnd.A) + { + return _locomotive.EnumerateCoupled().First().WheelBoundsA; + } + + return _locomotive.EnumerateCoupled(Car.LogicalEnd.B).First().WheelBoundsB.Flipped(); + } + + private static float CalculateTotalLength(BaseLocomotive selectedLoco) + { + List coupledCarsCached = selectedLoco.EnumerateCoupled().ToList(); + float num = 0f; + foreach (Car item in coupledCarsCached) + { + num += item.carLength; + } + + return num + 1.04f * (float)(coupledCarsCached.Count - 1); + } + + private static void AddObserversToCar(AutoEngineerWaypointControls __instance, Car c) + { + AddObserver(__instance, c, Car.KeyOpsWaybill); + AddObserver(__instance, c, Car.KeyOpsRepairDestination); + foreach (Car.LogicalEnd logicalEnd in CarInspector_PopulateCarPanel_Patch.ends) + { + AddObserver(__instance, c, Car.KeyValueKeyFor(Car.EndGearStateKey.IsCoupled, c.LogicalToEnd(logicalEnd)), true); + } + } + + private static void AddObserver(AutoEngineerWaypointControls __instance, Model.Car car, string key, bool clearCarCache = false) + { + _keyChangeObservers.Add( + car.KeyValueObject.Observe( + key, + delegate (Value value) + { + _log.Information($"{car.DisplayName} OBSV {key}: {value}; recalcing {recalcing}"); + if (recalcing) return; + try + { + foreach(var o in _keyChangeObservers) + { + o.Dispose(); + } + var loco = TrainController.Shared.SelectedLocomotive; + TrainController.Shared.SelectedCar = null; + _keyChangeObservers.Clear(); + recalcing = true; + new WaitForSeconds(0.25f); + TrainController.Shared.SelectedCar = loco; + } + catch (Exception ex) + { + _log.ForContext("car", car).Warning(ex, $"{nameof(AddObserver)} {car} Exception logged for {key}"); + } + }, + false + ) + ); + } +} diff --git a/TweaksAndThings/Patches/BaseLocomotive_FindSourceLocomotive_Patch.cs b/TweaksAndThings/Patches/BaseLocomotive_FindSourceLocomotive_Patch.cs index bc2db78..887d234 100644 --- a/TweaksAndThings/Patches/BaseLocomotive_FindSourceLocomotive_Patch.cs +++ b/TweaksAndThings/Patches/BaseLocomotive_FindSourceLocomotive_Patch.cs @@ -43,3 +43,89 @@ internal class BaseLocomotive_FindSourceLocomotive_Patch return null; } } + +//[HarmonyPatch(typeof(LocomotiveAirSystem))] +//[HarmonyPatch(nameof(LocomotiveAirSystem._ShouldDeferToLocomotiveAir))] +//[HarmonyPatchCategory("RMROC451TweaksAndThings")] +//internal class LocomotiveAirSystem__ShouldDeferToLocomotiveAir_Patch +//{ +// private static void Postfix(LocomotiveAirSystem __instance, ref LocomotiveAirSystem locomotiveAirSystem, ref bool __result) +// { +// TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; +// if (!tweaksAndThings.IsEnabled()) return; + +// __result = _ShouldDeferToLocomotiveAir(__instance, out locomotiveAirSystem); +// } +// public static bool _ShouldDeferToLocomotiveAir(LocomotiveAirSystem __instance, out LocomotiveAirSystem locomotiveAirSystem) +// { +// locomotiveAirSystem = null; +// if (__instance.car.set == null) +// { +// return false; +// } +// if (!(__instance.car.air is LocomotiveAirSystem locomotiveAirSystem2) || !(__instance.car is BaseLocomotive baseLocomotive)) +// { +// return false; +// } +// if (!locomotiveAirSystem2.IsCutOut || locomotiveAirSystem2.IsMuEnabled) +// { +// return false; +// } +// BaseLocomotive baseLocomotive2 = baseLocomotive.FindMuSourceLocomotive(); +// if (baseLocomotive2 == null) +// { +// return false; +// } +// if (!(baseLocomotive2.air is LocomotiveAirSystem locomotiveAirSystem3)) +// { +// return false; +// } +// locomotiveAirSystem = locomotiveAirSystem3; +// return true; +// } +//} + +//[HarmonyPatch(typeof(CarAirSystem))] +//[HarmonyPatch(nameof(CarAirSystem.ShouldDeferToLocomotiveAir))] +//[HarmonyPatchCategory("RMROC451TweaksAndThings")] +//internal class CarAirSystem_ShouldDeferToLocomotiveAir_Patch +//{ +// private static void Postfix(CarAirSystem __instance, ref LocomotiveAirSystem locomotiveAirSystem, ref bool __result) +// { +// TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; +// if (!tweaksAndThings.IsEnabled()) return; + +// __result = ShouldDeferToLocomotiveAir(__instance, out locomotiveAirSystem); +// } + +// public static bool ShouldDeferToLocomotiveAir(CarAirSystem __instance, out LocomotiveAirSystem locomotiveAirSystem) +// { +// locomotiveAirSystem = null; +// if (__instance.car.set == null) +// { +// return false; +// } +// if (__instance.car.Archetype != CarArchetype.Tender) +// { +// return false; +// } +// if (!__instance.car.TryGetAdjacentCar(__instance.car.EndToLogical(Car.End.F), out var adjacent) || !adjacent.IsLocomotive) +// { +// return false; +// } +// if (!(adjacent.air is LocomotiveAirSystem locomotiveAirSystem2)) +// { +// return false; +// } +// locomotiveAirSystem = locomotiveAirSystem2; +// if (locomotiveAirSystem.IsMuEnabled) +// { +// return true; +// } +// if (!locomotiveAirSystem.IsCutOut) +// { +// return true; +// } +// return locomotiveAirSystem.ShouldDeferToLocomotiveAir(out locomotiveAirSystem); +// } +//} diff --git a/TweaksAndThings/Patches/CarInspector_PopulateCarPanel_Patch.cs b/TweaksAndThings/Patches/CarInspector_PopulateCarPanel_Patch.cs index ecd6ae8..ed52750 100644 --- a/TweaksAndThings/Patches/CarInspector_PopulateCarPanel_Patch.cs +++ b/TweaksAndThings/Patches/CarInspector_PopulateCarPanel_Patch.cs @@ -28,7 +28,7 @@ namespace RMROC451.TweaksAndThings.Patches; internal class CarInspector_PopulateCarPanel_Patch { private static ILogger _log => Log.ForContext(); - private static IEnumerable ends = Enum.GetValues(typeof(LogicalEnd)).Cast(); + internal static IEnumerable ends = Enum.GetValues(typeof(LogicalEnd)).Cast(); /// /// If a caboose inspector is opened, it will auto set Anglecocks, gladhands and hand brakes @@ -181,7 +181,7 @@ internal class CarInspector_PopulateCarPanel_Patch case MrocHelperType.BleedAirSystem: consist = consist.Where(c => !c.MotivePower()); - _log.ForContext("car", car).Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}"); + _log.ForContext("car", car).Debug($"{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)); @@ -190,7 +190,7 @@ internal class CarInspector_PopulateCarPanel_Patch case MrocHelperType.Oil: consist = consist.Where(c => c.NeedsOiling || c.HasHotbox); - _log.ForContext("car", car).Information($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}"); + _log.ForContext("car", car).Debug($"{car} => {mrocHelperType} => {string.Join("/", consist.Select(c => c.ToString()))}"); foreach (Model.Car oil in consist) { StateManager.ApplyLocal(new PropertyChange(oil.id, nameof(Car.Oiled).ToLower(), new FloatPropertyValue(1))); @@ -232,7 +232,7 @@ internal class CarInspector_PopulateCarPanel_Patch 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)"); + _log.ForContext("car", car).Debug($"{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)})"); diff --git a/TweaksAndThings/Patches/CarPickable_Activate_Patch.cs b/TweaksAndThings/Patches/CarPickable_Activate_Patch.cs index f20795a..32dfc7d 100644 --- a/TweaksAndThings/Patches/CarPickable_Activate_Patch.cs +++ b/TweaksAndThings/Patches/CarPickable_Activate_Patch.cs @@ -2,6 +2,7 @@ using HarmonyLib; using Model; using Model.Ops; +using Model.Physics; using Network; using Railloader; using RMROC451.TweaksAndThings.Enums; @@ -9,6 +10,7 @@ using RMROC451.TweaksAndThings.Extensions; using RollingStock; using Serilog; using System; +using System.Collections.Generic; using System.Linq; using UI; using UI.Tags; @@ -40,92 +42,179 @@ internal class CarPickable_Activate_Patch { TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; if (!tweaksAndThings.IsEnabled()) return true; - bool bCtrlAltHeld = GameInput.IsControlDown && GameInput.IsAltDown; - _log.ForContext("car", __instance.car).Information($"{GameInput.IsShiftDown} {GameInput.IsControlDown} {GameInput.IsAltDown} {bCtrlAltHeld} "); + _log.ForContext("car", __instance.car).Debug($"{GameInput.IsShiftDown} {GameInput.IsControlDown} {GameInput.IsAltDown}"); if (OnPointerDown(evt, PickableActivation.Primary)) { CameraSelector.shared.FollowCar(__instance.car); - _log.ForContext("car", __instance.car).Information("just click!"); + _log.ForContext("car", __instance.car).Debug("just click!"); return false; - } - //single click with keys pressed: - else if (evt.Activation == PickableActivation.Primary) - { - bool output = true; - var consist = __instance.car.EnumerateCoupled(); - bool handbrakesApplied = consist.Any(c => c.HandbrakeApplied()); - bool airSystemIssues = consist.Any(c => c.EndAirSystemIssue()); - Func cabooseNear = () => (bool)__instance.car.FindMyCaboose(0.0f, false); - bool needsOiling = GameInput.IsShiftDown && consist.All(c => c.IsStopped()) && consist.Any(c => c.NeedsOiling || c.HasHotbox) && (!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || cabooseNear()); - var chargeIt = handbrakesApplied || airSystemIssues || needsOiling; - //CTRL + ALT + SHIFT : BrakesAngleCocksAndOiling - //CTRL + ALT : Release Consist Brakes and Check AngleCocks - //ALT + SHIFT : toggle consist brakes - //CTRL + SHIFT : Check Consist Angle Cocks - //ALT : Toggle car brakes and & air up cars - //CTRL : NOTHING; BASE CAR INSPECTOR + } + return HandleCarOrTrainBrakeDisplayClick(__instance.car, tweaksAndThings, evt.Activation); + } + internal static bool HandleCarOrTrainBrakeDisplayClick(Car car, TweaksAndThingsPlugin tweaksAndThings, PickableActivation activation) + { + bool bCtrlAltHeld = GameInput.IsControlDown && GameInput.IsAltDown; + bool output = true; + var consist = car.EnumerateCoupled(); + bool handbrakesApplied = consist.Any(c => c.HandbrakeApplied()); + bool airSystemIssues = consist.Any(c => c.EndAirSystemIssue()); + Func cabooseNear = () => (bool)car.FindMyCaboose(0.0f, false); + bool needsOiling = GameInput.IsShiftDown && consist.All(c => c.IsStopped()) && consist.Any(c => c.NeedsOiling || c.HasHotbox) && (!tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || cabooseNear()); + var chargeIt = handbrakesApplied || airSystemIssues || needsOiling; + //CTRL + ALT + SHIFT : BrakesAngleCocksAndOiling + //CTRL + ALT : Release Consist Brakes and Check AngleCocks + //ALT + SHIFT : toggle consist brakes + //CTRL + SHIFT : Check Consist Angle Cocks + //ALT : Toggle car brakes and & air up cars + //CTRL : NOTHING; BASE CAR INSPECTOR + //SHIFT : FOLLOW + + if (activation == PickableActivation.Primary) + { if (bCtrlAltHeld) { - BrakesAngleCocksAndOiling(__instance, tweaksAndThings, GameInput.IsShiftDown, consist, handbrakesApplied, airSystemIssues, needsOiling, chargeIt); - _log.ForContext("car", __instance.car).Information($"ctrlAlt{(GameInput.IsShiftDown ? "shift" : string.Empty)}Held!"); + BrakesAngleCocksAndOiling(car, tweaksAndThings, GameInput.IsShiftDown, consist, handbrakesApplied, airSystemIssues, needsOiling, chargeIt); + _log.ForContext("car", car).Debug($"ctrlAlt{(GameInput.IsShiftDown ? "shift" : string.Empty)}Held!"); output = false; } else if (GameInput.IsAltDown && GameInput.IsShiftDown) { - CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment()); - _log.ForContext("car", __instance.car).Information("ctrlShiftHeld!"); + CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment()); + _log.ForContext("car", car).Debug("ctrlShiftHeld!"); output = false; } else if (GameInput.IsControlDown && GameInput.IsShiftDown) { if (airSystemIssues) - CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.GladhandAndAnglecock, tweaksAndThings.EndGearHelpersRequirePayment()); - _log.ForContext("car", __instance.car).Information("altShiftHeld!"); + CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, tweaksAndThings.EndGearHelpersRequirePayment()); + _log.ForContext("car", car).Debug("altShiftHeld!"); output = false; } else if (GameInput.IsAltDown) { - __instance.car.SetHandbrake(!__instance.car.HandbrakeApplied()); - CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(__instance.car); - if (__instance.car.TryGetAdjacentCar(Car.LogicalEnd.A, out Model.Car cA)) CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(cA); - if (__instance.car.TryGetAdjacentCar(Car.LogicalEnd.B, out Model.Car cB)) CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(cB); - _log.ForContext("car", __instance.car).Information("ctrlHeld!"); - TagController.Shared.UpdateTag(__instance.car, __instance.car.TagCallout, OpsController.Shared); - __instance.car.TagCallout.Update(); + car.SetHandbrake(!car.HandbrakeApplied()); + CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(car); + if (car.TryGetAdjacentCar(Car.LogicalEnd.A, out Model.Car cA)) CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(cA); + if (car.TryGetAdjacentCar(Car.LogicalEnd.B, out Model.Car cB)) CarInspector_PopulateCarPanel_Patch.CarEndAirUpdate(cB); + _log.ForContext("car", car).Debug("ctrlHeld!"); + TagController.Shared.UpdateTag(car, car.TagCallout, OpsController.Shared); + car.TagCallout.Update(); output = false; } - return output; - - - //else if (ctrlHeld && shiftHeld) - //{ - // var selected = UI.CarInspector.CarInspector._instance._selectedTabState.Value; - // UI.CarInspector.CarInspector.Show(__instance.car); - // UI.CarInspector.CarInspector._instance._selectedTabState.Value = selected; - - //} + else if (GameInput.IsShiftDown) + { + CameraSelector.shared.FollowCar(car); + output = false; + } + } + else if ((GameInput.IsControlDown || GameInput.IsAltDown) && activation == PickableActivation.Secondary) + { + AltClickageMyBrosif(car, activation); + output = false; + } + else if (activation == PickableActivation.Secondary) + { + //AltClickageMyBrosif(car, activation); + CarPickable.HandleShowContextMenu(car); + output = false; } - return true; + return output; } - private static void BrakesAngleCocksAndOiling(CarPickable __instance, TweaksAndThingsPlugin tweaksAndThings, bool shiftHeld, System.Collections.Generic.IEnumerable consist, bool handbrakesApplied, bool airSystemIssues, bool needsOiling, bool chargeIt) + private static void AltClickageMyBrosif(Car car, PickableActivation activation) + { + //if (GameInput.IsControlDown) + //{ + + //} + //else if (GameInput.IsAltDown) + //{ + // logSet(car); + // //car.TryGetAdjacentCar(Car.LogicalEnd.A, out var OutCarA); + // //car.TryGetAdjacentCar(Car.LogicalEnd.B, out var OutCarB); + // output = false; + //} + List cars = TrainController.Shared.SelectedLocomotive.EnumerateCoupled().ToList(); + logSet(car); + //car.TryGetAdjacentCar(Car.LogicalEnd.A, out var OutCarA); + //car.TryGetAdjacentCar(Car.LogicalEnd.B, out var OutCarB); + //bool b4 = false; + //if ((Object)(object)OutCarA != (Object)null && OutCarA.id == cars[i - 1].id) + //{ + // cars[i - 1].ApplyEndGearChange(Car.LogicalEnd.B, Car.EndGearStateKey.Anglecock, 0f); + // ccar.ApplyEndGearChange(Car.LogicalEnd.A, Car.EndGearStateKey.Anglecock, 0f); + // ccar.HandleCouplerClick(ccar.EndGearA.Coupler); + // logger.Information("{indName}: Decoupled {ccarName} from {cars1Name} at NOA{FR}", new object[4] + // { + // ((Object)base.Industry).name, + // ccar.DisplayName, + // cars[i - 1].DisplayName, + // (ccar.LogicalToEnd(Car.LogicalEnd.A) == Car.End.F) ? "F" : "R" + // }); + // Storage.LastCarEnd = ccar.LogicalToEnd(Car.LogicalEnd.A); + // b4 = true; + //} + //if ((Object)(object)OutCarB != (Object)null && !b4 && OutCarB.id == cars[i - 1].id) + //{ + // cars[i - 1].ApplyEndGearChange(Car.LogicalEnd.A, Car.EndGearStateKey.Anglecock, 0f); + // ccar.ApplyEndGearChange(Car.LogicalEnd.B, Car.EndGearStateKey.Anglecock, 0f); + // ccar.HandleCouplerClick(ccar.EndGearB.Coupler); + // logger.Information("{indName}: Decoupled {ccarName} from {carsm1Name} at NOB{FR}", new object[4] + // { + // ((Object)base.Industry).name, + // ccar.DisplayName, + // cars[i - 1].DisplayName, + // (ccar.LogicalToEnd(Car.LogicalEnd.A) == Car.End.F) ? "F" : "R" + // }); + // Storage.LastCarEnd = ccar.LogicalToEnd(Car.LogicalEnd.B); + // b4 = true; + //} + //if (!b4) + //{ + // logger.Information("{IndName}: ERROR: NLC {ccarName} can't find LCC {carsm1Name} ({OutCarAName},{OutCarBName})", new object[5] + // { + // ((Object)base.Industry).name, + // ccar.DisplayName, + // cars[i - 1].DisplayName, + // ((Object)(object)OutCarA != (Object)null) ? OutCarA.DisplayName : "no Car", + // ((Object)(object)OutCarB != (Object)null) ? OutCarB.DisplayName : "no Car" + // }); + // return; + //} + } + + private static void logSet(Car refCar) + { + IntegrationSet set = refCar.set; + foreach (Car car in set.Cars) + { + Car acar; + string endA = (car.TryGetAdjacentCar(Car.LogicalEnd.A, out acar) ? acar.DisplayName : "none"); + Car bcar; + string endB = (car.TryGetAdjacentCar(Car.LogicalEnd.B, out bcar) ? bcar.DisplayName : "none"); + _log.Information("[Car: {id}:(EndA:{carA}),(EndB:{carB}),(FisA:{fisA})]", new object[4] { car.DisplayName, endA, endB, car.FrontIsA }); + } + } + + + private static void BrakesAngleCocksAndOiling(Car car, TweaksAndThingsPlugin tweaksAndThings, bool shiftHeld, System.Collections.Generic.IEnumerable consist, bool handbrakesApplied, bool airSystemIssues, bool needsOiling, bool chargeIt) { int hbFix = 0; if (handbrakesApplied) - CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.Handbrake, false); + CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, false); if (airSystemIssues) - CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.GladhandAndAnglecock, false); + CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.GladhandAndAnglecock, false); if (needsOiling) - hbFix = CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(__instance.car, MrocHelperType.Oil, false); + hbFix = CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Oil, false); if (hbFix > 0) - Multiplayer.Broadcast($"Near {Hyperlink.To(__instance.car)}: \"{hbFix.Pluralize("hotbox") + " repaired!"}\""); + Multiplayer.Broadcast($"Near {Hyperlink.To(car)}: \"{hbFix.Pluralize("hotbox") + " repaired!"}\""); if (chargeIt) - CarInspector_PopulateCarPanel_Patch.CalculateCostIfEnabled(__instance.car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment(), consist); + CarInspector_PopulateCarPanel_Patch.CalculateCostIfEnabled(car, MrocHelperType.Handbrake, tweaksAndThings.EndGearHelpersRequirePayment(), consist); } public static bool OnPointerDown( diff --git a/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs b/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs index fc174b0..0b36b59 100644 --- a/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs +++ b/TweaksAndThings/Patches/CarPickable_HandleShowContextMenu_Patch.cs @@ -44,23 +44,24 @@ internal class CarPickable_HandleShowContextMenu_Patch CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.BleedAirSystem, buttonsHaveCost); }); } - - shared.AddButton(ContextMenuQuadrant.Brakes, $"{(car.EnumerateCoupled().Any(c => c.HandbrakeApplied()) ? "Release " : "Set ")} Consist", SpriteName.Handbrake, delegate - { - CarInspector_PopulateCarPanel_Patch.MrocConsistHelper(car, MrocHelperType.Handbrake, buttonsHaveCost); - }); - if (car.EnumerateCoupled().Any(c => c.EndAirSystemIssue())) - { + shared.AddButton(ContextMenuQuadrant.Brakes, $"{(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); }); + } } - } - else { - if (car.SupportsBleed()) + else { + if (car.SupportsBleed()) + { shared.AddButton(ContextMenuQuadrant.Brakes, "Bleed", SpriteName.Bleed, car.SetBleed); } shared.AddButton(ContextMenuQuadrant.Brakes, car.air.handbrakeApplied ? "Release Handbrake" : "Apply Handbrake", SpriteName.Handbrake, delegate diff --git a/TweaksAndThings/Patches/MapWindow_OnClick_Patch.cs b/TweaksAndThings/Patches/MapWindow_OnClick_Patch.cs index 5619df3..c1aef99 100644 --- a/TweaksAndThings/Patches/MapWindow_OnClick_Patch.cs +++ b/TweaksAndThings/Patches/MapWindow_OnClick_Patch.cs @@ -39,7 +39,7 @@ internal class MapWindow_OnClick_Patch texture2D.wrapMode = TextureWrapMode.Clamp; if (!ImageConversion.LoadImage(texture2D, File.ReadAllBytes(path))) { - _log.Information($"Unable to load {name} icon!"); + _log.Debug($"Unable to load {name} icon!"); return null; } Sprite sprite = Sprite.Create(texture2D, new Rect(0f, 0f, texture2D.width, texture2D.height), new Vector2(0.5f, 0.5f)); diff --git a/TweaksAndThings/Patches/NoticeExtensions_PostNotice_Patch.cs b/TweaksAndThings/Patches/NoticeExtensions_PostNotice_Patch.cs index 27fe48c..17f0d19 100644 --- a/TweaksAndThings/Patches/NoticeExtensions_PostNotice_Patch.cs +++ b/TweaksAndThings/Patches/NoticeExtensions_PostNotice_Patch.cs @@ -1,4 +1,5 @@ using Game.Notices; +using Game.State; using HarmonyLib; using Model; using Serilog; @@ -14,10 +15,11 @@ internal class NoticeExtensions_PostNotice_Patch private static ILogger _log => Log.ForContext(); static void Postfix(Car car, string key, string content) { + if (!StateManager.IsHost) return; try { - //Log.Information($"{car.DisplayName} patch PostNotice"); + //Log.Debug($"{car.DisplayName} patch PostNotice"); if (!string.IsNullOrEmpty(content) && key.Equals("ai-wpt") && content.ToLower().Contains("Arrived at Waypoint".ToLower()) diff --git a/TweaksAndThings/Patches/OpsController_AnnounceCoalescedPayments_Patch.cs b/TweaksAndThings/Patches/OpsController_AnnounceCoalescedPayments_Patch.cs index 337045a..6692686 100644 --- a/TweaksAndThings/Patches/OpsController_AnnounceCoalescedPayments_Patch.cs +++ b/TweaksAndThings/Patches/OpsController_AnnounceCoalescedPayments_Patch.cs @@ -113,18 +113,18 @@ internal class OpsController_AnnounceCoalescedPayments_Patch 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)}"); + //Log.Debug($"{nameof(OpsController_AnnounceCoalescedPayments_Patch)} => Caboose Helper => PassengerStops => {string.Join(",", passengerStops)}"); var cabeese = passengerStops .SelectMany(t => t.TrackSpans?.Select(s => (tc.CarsOnSpan(s) ?? Enumerable.Empty()).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}") ?? [])}"); + //Log.Debug($"{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}"); + //Log.Debug($"{nameof(OpsController_AnnounceCoalescedPayments_Patch)} => Caboose Helper ({deltaTime}) => {caboose.t} : {caboose.c2}"); CarLoadCrewHelper(caboose.c2, deltaTime); } dateTime = TimeWeather.Now; diff --git a/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs b/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs index 724e5b1..971dc0d 100644 --- a/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs +++ b/TweaksAndThings/Patches/TagController_UpdateTag_Patch.cs @@ -28,7 +28,7 @@ internal class TagController_UpdateTag_Patch return; } - ProceedWithPostFix(car, tagCallout, tweaksAndThings.RequireConsistCabooseForOilerAndHotboxSpotter() || !tweaksAndThings.CabooseRequiredForLocoOilIndicator()); + ProceedWithPostFix(car, tagCallout, tweaksAndThings.CabooseRequiredForLocoOilIndicator()); return; } diff --git a/TweaksAndThings/Patches/TrainBrakeDisplay_GetCarImage_Patch.cs b/TweaksAndThings/Patches/TrainBrakeDisplay_GetCarImage_Patch.cs new file mode 100644 index 0000000..05d80a4 --- /dev/null +++ b/TweaksAndThings/Patches/TrainBrakeDisplay_GetCarImage_Patch.cs @@ -0,0 +1,42 @@ +using HarmonyLib; +using Model; +using Railloader; +using UI; +using UI.Tags; +using UnityEngine; + +namespace RMROC451.TweaksAndThings.Patches; + +[HarmonyPatch(typeof(TrainBrakeDisplay))] +[HarmonyPatch(nameof(TrainBrakeDisplay.Update))] +[HarmonyPatchCategory("RMROC451TweaksAndThings")] +internal class TrainBrakeDisplay_Update_Patch() +{ + private static bool Prefix(TrainBrakeDisplay __instance) + { + + TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; + if (!tweaksAndThings.IsEnabled()) return true; + + TweakedOriginalMethods.TrainBrakeDisplay.Update(__instance); + + return false; + } +} + +[HarmonyPatch(typeof(TrainBrakeDisplay))] +[HarmonyPatch(nameof(TrainBrakeDisplay.ColorForCar))] +[HarmonyPatchCategory("RMROC451TweaksAndThings")] +internal class TrainBrakeDisplay_ColorForCar_Patch +{ + private static bool Prefix(TrainBrakeDisplay __instance, Car car, ref Color __result) + { + TweaksAndThingsPlugin tweaksAndThings = SingletonPluginBase.Shared; + if (!tweaksAndThings.IsEnabled() || !tweaksAndThings.TrainBrakeDisplayShowsColorsInCalloutMode() || !TagController.Shared.TagsVisible) return true; + + TweakedOriginalMethods.TrainBrakeDisplay.ColorForCar(__instance, car, ref __result); + + return false; + } +} + diff --git a/TweaksAndThings/RMROC451.TweaksAndThings.csproj b/TweaksAndThings/RMROC451.TweaksAndThings.csproj index c4d70df..8069656 100644 --- a/TweaksAndThings/RMROC451.TweaksAndThings.csproj +++ b/TweaksAndThings/RMROC451.TweaksAndThings.csproj @@ -1,9 +1,8 @@  - + + - - False False diff --git a/TweaksAndThings/Settings/Settings.cs b/TweaksAndThings/Settings/Settings.cs index de4b2a1..26ff960 100644 --- a/TweaksAndThings/Settings/Settings.cs +++ b/TweaksAndThings/Settings/Settings.cs @@ -29,7 +29,8 @@ public class Settings bool servicingFundPenalty, bool safetyFirst, CrewHourLoadMethod loadCrewHoursMethod, - float cabeeseSearchRadiusFtInMeters + float cabeeseSearchRadiusFtInMeters, + bool trainBrakeDisplayShowsColorsInCalloutMode ) { WebhookSettingsList = webhookSettingsList; @@ -43,6 +44,7 @@ public class Settings SafetyFirst = safetyFirst; LoadCrewHoursMethod = loadCrewHoursMethod; CabeeseSearchRadiusFtInMeters = cabeeseSearchRadiusFtInMeters; + TrainBrakeDisplayShowsColorsInCalloutMode = trainBrakeDisplayShowsColorsInCalloutMode; } public readonly UIState _selectedTabState = new UIState(null); @@ -57,6 +59,7 @@ public class Settings public bool SafetyFirst; public CrewHourLoadMethod LoadCrewHoursMethod; public float CabeeseSearchRadiusFtInMeters; + public bool TrainBrakeDisplayShowsColorsInCalloutMode; internal void AddAnotherRow() { @@ -64,7 +67,7 @@ public class Settings 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"); + Log.Debug($"Adding another {nameof(WebhookSettings)} list entry, last one was filled in"); } } } @@ -135,7 +138,7 @@ public static class SettingsExtensions input?.settings?.SafetyFirst ?? false; public static bool DayLoadCrewHours(this TweaksAndThingsPlugin input) => (input?.settings?.LoadCrewHoursMethod ?? CrewHourLoadMethod.Tracks) == CrewHourLoadMethod.Daily; - public static float CabeeseSearchRadiusInMeters(this TweaksAndThingsPlugin input) => - input?.settings?.CabeeseSearchRadiusFtInMeters ?? 21f; + public static bool TrainBrakeDisplayShowsColorsInCalloutMode(this TweaksAndThingsPlugin input) => + input?.settings?.TrainBrakeDisplayShowsColorsInCalloutMode ?? false; } \ No newline at end of file diff --git a/TweaksAndThings/TweakedOriginalMethods/TrainBrakeDisplay.cs b/TweaksAndThings/TweakedOriginalMethods/TrainBrakeDisplay.cs new file mode 100644 index 0000000..08ad136 --- /dev/null +++ b/TweaksAndThings/TweakedOriginalMethods/TrainBrakeDisplay.cs @@ -0,0 +1,177 @@ +using Model; +using Model.Ops; +using Model.Physics; +using Railloader; +using RMROC451.TweaksAndThings.Patches; +using UI; +using UI.CarInspector; +using UI.Common; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; +using static UnityEngine.UI.Image; +using Object = UnityEngine.Object; + +namespace RMROC451.TweaksAndThings.TweakedOriginalMethods +{ + internal static class TrainBrakeDisplay + { + internal static void ColorForCar(UI.TrainBrakeDisplay __instance, Car car, ref Color __result) + { + __result = Color.gray; + + if (car.IsDerailed) + { + __result = __instance.derailedColor; + return; + } + CarAirSystem air = car.air; + OpsController opsController = OpsController.Shared; + if (air.handbrakeApplied) + { + __result = __instance.handbrakeAppliedColor; + return; + } + else if ( + (Object)(object)opsController != (Object)null && + opsController.TryGetDestinationInfo(car, out var destinationName, out var isAtDestination, out var destinationPosition, out var destination) + ) + { + Area area = opsController.AreaForCarPosition(destination); + if ((Object)(object)area != (Object)null) + { + __result = area.tagColor; + } + if (!isAtDestination) + { + float num = 1f / __result.maxColorComponent; + __result *= num; + } + } + + if (Mathf.InverseLerp(0f, 72f, air.BrakeCylinder.Pressure) >= 10f) __result = (__result + __instance.ColorForPsi(air.BrakeCylinder.Pressure)) / 2f; + } + + internal static Image GetCarImage(UI.TrainBrakeDisplay __instance, int index, float xCyl, float yCyl, Car car) + { + Image output = null; + if (__instance._carImages.Count - 1 < index) + { + var tweaksAndThings = SingletonPluginBase.Shared; + GameObject val = new GameObject(); + val.transform.SetParent(((Component)__instance).transform, false); + ((Object)val).name = $"Car {index}"; + float height = 12f; + val.AddComponent().SetFrame(xCyl, yCyl, __instance._imageWidth, height); + EventTrigger trigger = val.AddComponent(); + EventTrigger.Entry entry = new EventTrigger.Entry(); + entry.eventID = EventTriggerType.PointerClick; + entry.callback.AddListener((eventData) => { + PointerEventData ped = (PointerEventData)eventData; + PickableActivation pa = PickableActivation.Primary; + if (ped.button == PointerEventData.InputButton.Right) pa = PickableActivation.Secondary; + + bool checkFurther = ped.button == PointerEventData.InputButton.Middle ? true : CarPickable_Activate_Patch.HandleCarOrTrainBrakeDisplayClick(car, tweaksAndThings, pa); + if (checkFurther & GameInput.IsControlDown) CarInspector.Show(car); + }); + trigger.triggers.Add(entry); + output = val.AddComponent(); + + output.type = (Type)1; + __instance._carImages.Add(output); + } + else + { + output = __instance._carImages[index]; + ((Component)output).gameObject.SetActive(true); + } + output.sprite = (car.IsLocomotive ? __instance.locomotiveTile : __instance.carTile); + return output; + } + + internal static void Update(UI.TrainBrakeDisplay __instance) + { + Car selectedCar = __instance._trainController.SelectedCar; + if (selectedCar == null || selectedCar.set == null) return; + + int numberOfCars = selectedCar.set.NumberOfCars; + if (numberOfCars != __instance._lastNumCars) + { + __instance.RemoveAllImages(); + int num = Mathf.Clamp(numberOfCars, 0, 100); + float num2 = (float)num * 8f + (float)(num - 1) * 1f; + if (num2 > __instance._rectTransform.rect.width) + { + float num3 = __instance._rectTransform.rect.width / num2; + __instance._imageWidth = 8f * num3; + __instance._spacing = 1f * num3; + } + else + { + __instance._imageWidth = 8f; + __instance._spacing = 1f; + } + + __instance._lastNumCars = numberOfCars; + } + + int num4 = 0; + float num5 = __instance._imageWidth / 2f; + float num6 = 0f; + float y = 12f - 2f * __instance._spacing; + Car.LogicalEnd logicalEnd = ((selectedCar.set.IndexOfCar(selectedCar).GetValueOrDefault(0) >= numberOfCars / 2) ? Car.LogicalEnd.B : Car.LogicalEnd.A); + Car.LogicalEnd end = ((logicalEnd == Car.LogicalEnd.A) ? Car.LogicalEnd.B : Car.LogicalEnd.A); + bool stop = false; + int carIndex = selectedCar.set.StartIndexForConnected(selectedCar, logicalEnd, IntegrationSet.EnumerationCondition.Coupled); + Car car; + while (!stop && (car = selectedCar.set.NextCarConnected(ref carIndex, logicalEnd, IntegrationSet.EnumerationCondition.Coupled, out stop)) != null && !(num5 > __instance._rectTransform.rect.width)) + { + Image carImage = TweakedOriginalMethods.TrainBrakeDisplay.GetCarImage(__instance, num4, num5, 0f, car); + num5 += __instance._imageWidth + __instance._spacing; + carImage.color = __instance.ColorForCar(car); + Image airImage = __instance.GetAirImage(num4, num6, y); + num6 += __instance._imageWidth + __instance._spacing; + Color color; + if (!car[logicalEnd].IsCoupled) + { + color = ColorForOuterAnglecock(car[logicalEnd].AnglecockSetting); + } + else + { + Car car2 = car.CoupledTo(logicalEnd); + bool num7 = car2 != null && car2[end].AnglecockSetting > 0.9f; + bool flag = car[logicalEnd].AnglecockSetting > 0.9f; + color = ((num7 && flag && car[logicalEnd].IsAirConnected) ? Color.clear : Color.white); + } + + airImage.color = color; + if (!car[end].IsCoupled) + { + __instance.GetAirImage(num4 + 1, num6, y).color = ColorForOuterAnglecock(car[end].AnglecockSetting); + } + + num4++; + } + + for (int i = num4; i < __instance._carImages.Count; i++) + { + __instance._carImages[i].gameObject.SetActive(value: false); + } + + for (int j = num4 + 1; j < __instance._airImages.Count; j++) + { + __instance._airImages[j].gameObject.SetActive(value: false); + } + + static Color ColorForOuterAnglecock(float value) + { + if (!((double)value < 0.01)) + { + return Color.white; + } + + return Color.clear; + } + } + } +} diff --git a/TweaksAndThings/TweaksAndThingsPlugin.cs b/TweaksAndThings/TweaksAndThingsPlugin.cs index 7a623b2..1cf6011 100644 --- a/TweaksAndThings/TweaksAndThingsPlugin.cs +++ b/TweaksAndThings/TweaksAndThingsPlugin.cs @@ -40,7 +40,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, static TweaksAndThingsPlugin() { - Log.Information("Hello! Static Constructor was called!"); + Log.Debug("Hello! Static Constructor was called!"); } public TweaksAndThingsPlugin(IModdingContext moddingContext, IModDefinition self) @@ -49,7 +49,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, this.moddingContext = moddingContext; - logger.Information("Hello! Constructor was called for {modId}/{modVersion}!", self.Id, self.Version); + logger.Debug("Hello! Constructor was called for {modId}/{modVersion}!", self.Id, self.Version); moddingContext.RegisterConsoleCommand(new EchoCommand()); @@ -58,7 +58,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, public override void OnEnable() { - logger.Information("OnEnable() was called!"); + logger.Debug("OnEnable() was called!"); var harmony = new Harmony(modDefinition.Id); harmony.PatchCategory(modDefinition.Id.Replace(".", string.Empty)); } @@ -77,7 +77,7 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, public void ModTabDidOpen(UIPanelBuilder builder) { - logger.Information("Daytime!"); + logger.Debug("Daytime!"); if (settings == null) settings = new(); if (!settings?.WebhookSettingsList?.Any() ?? true) settings.WebhookSettingsList = new[] { new WebhookSettings() }.ToList(); @@ -88,7 +88,9 @@ public class TweaksAndThingsPlugin : SingletonPluginBase, settings?.WebhookSettingsList.SanitizeEmptySettings(); builder.AddSection("Adjustments To Base Game", (UIPanelBuilder builder) => { - builder.AddLabel("Repair tracks now require cars to be waybilled, or they will not be serviced/overhauled.\nThey will report on the company window's location section as 'No Work Order Assigned'."); + builder.AddLabel("1) Repair tracks now require cars to be waybilled, or they will not be serviced/overhauled.\nThey will report on the company window's location section as 'No Work Order Assigned'."); + builder.Spacer(spacing * spacing); + builder.AddLabel("2) You now have the same click options on the little car icons in the lower left engine controls ui, as you do with cars in the game. Ctrl click -> open car inspector, etc."); }); builder.Spacer(spacing * spacing); builder.AddTabbedPanels(settings._selectedTabState, delegate (UITabbedPanelBuilder tabBuilder) @@ -236,6 +238,18 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - } ).Tooltip("Allow Insufficient Funds", $@"Will allow interchange service and repair shops to still function when you are insolvent, at a 20% overdraft fee."); + builder.Spacer(spacing); + builder.AddFieldToggle( + "Train Brake Color Mode", + () => this.TrainBrakeDisplayShowsColorsInCalloutMode(), + delegate (bool enabled) + { + if (settings == null) settings = new(); + settings.TrainBrakeDisplayShowsColorsInCalloutMode = enabled; + builder.Rebuild(); + } + ).Tooltip("Train Brake Color Mode", $@"When enabled/checked and car tag callout mode is enabled (showing car tags hovering over them), the train brake display of the selected locomotive will change the cars/engines to their destination area's color to help you visualize sets of cars at a glance."); + builder.Spacer(spacing); EngineRosterShowsFuelStatusUISection(builder); } @@ -256,7 +270,7 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - builder.Rebuild(); } ) - ).Tooltip("Enable Fuel Display in Engine Roster", $"Will add reaming fuel indication to Engine Roster (with details in roster row tool tip), Examples : {string.Join(" ", Enumerable.Range(0, 4).Select(i => TextSprites.PiePercent(i, 4)))}"); + ).Tooltip("Enable Fuel Display in Engine Roster", $"Will add reamaing 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.Spacer(spacing); builder.AddFieldToggle( @@ -333,7 +347,7 @@ AutoHotboxSpotter Update: decrease the random wait from 30 - 300 seconds to 15 - public void ModTabDidClose() { - logger.Information("Nighttime..."); + logger.Debug("Nighttime..."); this.moddingContext.SaveSettingsData(this.modDefinition.Id, settings ?? new()); } }