From 8c1b01db6b78f2e6136f285eee270914f9bd7968 Mon Sep 17 00:00:00 2001 From: pacoito123 Date: Sat, 3 Aug 2024 20:31:20 -0600 Subject: [PATCH] [v2.3.2] Compatibility with TerminalFormatter's modified store, among other things. - Added `TerminalFormatter` as a soft dependency. - _I think?_ - Sales for rotating items should now display with `TerminalFormatter` installed. - Added transpiler for `Store.GetNodeText()` to display discounted prices and amounts in the store page whenever an item is on sale. - Temporary fix until proper compatibility can be made. - `relativeScroll` now unpatches itself if `TerminalFormatter` is installed, since it already includes it. - Patching is done upon loading the main menu for the first time. --- CHANGELOG.md | 13 ++- .../TerminalFormatterCompatibility.cs | 79 +++++++++++++++++++ .../Patches/SyncShipUnlockablesPatches.cs | 20 +++++ .../Patches/TerminalItemSalesPatches.cs | 2 +- .../Patches/UnlockShipObjectPatches.cs | 2 +- StoreRotationConfig/Plugin.cs | 22 +++++- .../StoreRotationConfig.csproj | 6 +- manifest.json | 2 +- 8 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 StoreRotationConfig/Compatibility/TerminalFormatterCompatibility.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index cb0c9ad..f6d76df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ +### [2.3.2] + +Compatibility with TerminalFormatter's modified store, among other things. +- Added [TerminalFormatter](https://thunderstore.io/c/lethal-company/p/mrov/TerminalFormatter) as a soft dependency. + - _I think?_ +- Sales for rotating items should now display with [TerminalFormatter](https://thunderstore.io/c/lethal-company/p/mrov/TerminalFormatter) installed. + - Added transpiler for `Store.GetNodeText()` to display discounted prices and amounts in the store page whenever an item is on sale. + - Temporary fix until proper compatibility can be made. +- `relativeScroll` now unpatches itself if [TerminalFormatter](https://thunderstore.io/c/lethal-company/p/mrov/TerminalFormatter) is installed, since it already includes it. +- Patching is done upon loading into the main menu for the first time. + ### [2.3.1] -Compatibility patch for Lategame Upgrades (and likely some moon-routing mods). +Compatibility patch for Lategame Upgrades (and likely other moon-routing price adjustment mods). - Fixed compatibility with [Lategame Upgrades](https://thunderstore.io/c/lethal-company/p/malco/Lategame_Upgrades)' `Efficient Engines` upgrade. - `Terminal.LoadNewNodeIfAffordable()` transpiler no longer removes instructions or touches `Terminal.totalCostOfItems` when routing to a moon. - Changed priority of `Terminal.LoadNewNodeIfAffordable()` to `High` (600), so it's applied earlier. diff --git a/StoreRotationConfig/Compatibility/TerminalFormatterCompatibility.cs b/StoreRotationConfig/Compatibility/TerminalFormatterCompatibility.cs new file mode 100644 index 0000000..d9a92b9 --- /dev/null +++ b/StoreRotationConfig/Compatibility/TerminalFormatterCompatibility.cs @@ -0,0 +1,79 @@ +using HarmonyLib; +using System.Collections.Generic; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using TerminalFormatter.Nodes; +using static StoreRotationConfig.Patches.TerminalItemSalesPatches; + +namespace StoreRotationConfig.Compatibility +{ + /// + /// Class handling compatibility with 'TerminalFormatter'. + /// + [HarmonyPatch] + internal static class TerminalFormatterCompatibility + { + /// + /// Whether 'TerminalFormatter' is present in the BepInEx Chainloader or not. + /// + public static bool Enabled + { + get + { + _enabled ??= BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey("TerminalFormatter"); + + return (bool)_enabled; + } + } + private static bool? _enabled; + + /// + /// Whether patches for 'TerminalFormatter' compatibility have already been applied or not. + /// + public static bool Patched { get; internal set; } = false; + + /// + /// Replaces parameter with delegate call to display item discounts and their modified prices. + /// + /// ... (TerminalFormatter.Nodes.Store:286) + /// consoleTable.AddRow(new object[] + /// { + /// text7, + /// string.Format("${0}", + /// // terminalNode2.itemCost), + /// -> call(terminalNode2)), + /// "" + /// }); + /// Iterator with original IL instructions. + /// Iterator with modified IL instructions. + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] + [HarmonyPatch(typeof(Store), nameof(Store.GetNodeText))] + [HarmonyTranspiler] + private static IEnumerable GetNodeTextTranspiler(IEnumerable instructions) + { + return new CodeMatcher(instructions).MatchForward(false, + new CodeMatch(OpCodes.Ldstr, "[DECORATIONS]")) + .MatchForward(false, + new(OpCodes.Ldfld, AccessTools.Field(typeof(TerminalNode), nameof(TerminalNode.itemCost))), + new(OpCodes.Box, operand: typeof(int))) + .SetInstructionAndAdvance(Transpilers.EmitDelegate((TerminalNode item) => + { + // Return full cost if 'salesChance' is disabled OR the 'RotationSales' dictionary doesn't contain a discount for the item about to be displayed. + if (Plugin.Settings.SALE_CHANCE == 0 || (RotationSales != null && !RotationSales.ContainsKey(item))) + { + return $"{item.itemCost}"; + } + + Plugin.StaticLogger.LogDebug($"Appending {RotationSales[item]} to {item.creatureName}..."); + + // Obtain discounted item price. + int discountedPrice = item.itemCost - (int)(item.itemCost * (RotationSales[item] / 100f)); + + // Return string containing the discounted price and discount amount to display in the store page. + return $"{discountedPrice} ({RotationSales[item]}% OFF!)"; + })) + .SetOperandAndAdvance(typeof(string)) + .InstructionEnumeration(); + } + } +} \ No newline at end of file diff --git a/StoreRotationConfig/Patches/SyncShipUnlockablesPatches.cs b/StoreRotationConfig/Patches/SyncShipUnlockablesPatches.cs index ceaf2aa..79e5bee 100644 --- a/StoreRotationConfig/Patches/SyncShipUnlockablesPatches.cs +++ b/StoreRotationConfig/Patches/SyncShipUnlockablesPatches.cs @@ -1,4 +1,6 @@ +using GameNetcodeStuff; using HarmonyLib; +using StoreRotationConfig.Compatibility; using Unity.Netcode; namespace StoreRotationConfig.Patches @@ -46,6 +48,24 @@ private static void MenuManagerStartPre() // Plugin.Settings.ConfigSynced = false; UnlockablesSynced = false; // ... + + // Handle 'TerminalFormatter' compatibility here since I can't seem to get it to load before my mod, despite the soft dependency. + if (TerminalFormatterCompatibility.Enabled && !TerminalFormatterCompatibility.Patched) + { + Plugin.StaticLogger.LogInfo($"Patching 'TerminalFormatter'..."); + + // Patch 'TerminalFormatter.Nodes.Store.GetNodeText' to display discounts assigned to the rotating store. + Plugin.Harmony.PatchAll(typeof(TerminalFormatterCompatibility)); + + // Unpatch 'relativeScroll' tweak (already present in 'TerminalFormatter'). + Plugin.Harmony.Unpatch(AccessTools.Method(typeof(PlayerControllerB), "ScrollMouse_performed"), + HarmonyPatchType.Transpiler, Plugin.GUID); + + // Toggle patched status to avoid running more than once (every menu reload). + TerminalFormatterCompatibility.Patched = true; + + Plugin.StaticLogger.LogInfo($"'TerminalFormatter' patched!"); + } } } } \ No newline at end of file diff --git a/StoreRotationConfig/Patches/TerminalItemSalesPatches.cs b/StoreRotationConfig/Patches/TerminalItemSalesPatches.cs index fd380f4..153dc5c 100644 --- a/StoreRotationConfig/Patches/TerminalItemSalesPatches.cs +++ b/StoreRotationConfig/Patches/TerminalItemSalesPatches.cs @@ -188,7 +188,7 @@ private static IEnumerable TextPostProcessTranspiler(IEnumerabl TerminalNode item = storeRotation[index]; // Return if 'salesChance' is disabled OR the 'RotationSales' dictionary doesn't contain a discount for the item about to be displayed. - if (Plugin.Settings.SALE_CHANCE == 0 || RotationSales == null || !RotationSales.ContainsKey(item)) + if (Plugin.Settings.SALE_CHANCE == 0 || (RotationSales != null && !RotationSales.ContainsKey(item))) { return; } diff --git a/StoreRotationConfig/Patches/UnlockShipObjectPatches.cs b/StoreRotationConfig/Patches/UnlockShipObjectPatches.cs index 9d29276..e94f4ff 100644 --- a/StoreRotationConfig/Patches/UnlockShipObjectPatches.cs +++ b/StoreRotationConfig/Patches/UnlockShipObjectPatches.cs @@ -7,7 +7,7 @@ namespace StoreRotationConfig.Patches /// Patches for removing purchased items from both current and future store rotations. /// [HarmonyPatch(typeof(StartOfRound))] - internal class UnlockShipObjectPatch + internal class UnlockShipObjectPatches { [HarmonyPatch("UnlockShipObject", typeof(int))] [HarmonyPrefix] diff --git a/StoreRotationConfig/Plugin.cs b/StoreRotationConfig/Plugin.cs index 153f631..1d2dfc5 100644 --- a/StoreRotationConfig/Plugin.cs +++ b/StoreRotationConfig/Plugin.cs @@ -1,8 +1,8 @@ using BepInEx; using BepInEx.Logging; using HarmonyLib; +using StoreRotationConfig.Patches; using System; -using System.Reflection; namespace StoreRotationConfig { @@ -11,11 +11,17 @@ namespace StoreRotationConfig /// [BepInPlugin(GUID, PLUGIN_NAME, VERSION)] [BepInDependency("com.sigurd.csync", "5.0.1")] + [BepInDependency("TerminalFormatter", BepInDependency.DependencyFlags.SoftDependency)] public class Plugin : BaseUnityPlugin { - internal const string GUID = "pacoito.StoreRotationConfig", PLUGIN_NAME = "StoreRotationConfig", VERSION = "2.3.1"; + internal const string GUID = "pacoito.StoreRotationConfig", PLUGIN_NAME = "StoreRotationConfig", VERSION = "2.3.2"; internal static ManualLogSource StaticLogger { get; private set; } + /// + /// Harmony instance for patching. + /// + internal static Harmony Harmony { get; private set; } + /// /// Plugin configuration instance. /// @@ -47,8 +53,18 @@ private void Awake() try { + // Initialize 'Config' and 'Harmony' instances. Settings = new(Config); - _ = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), $"{GUID}"); + Harmony = new(GUID); + // + + // Apply all patches, except for compatibility ones. + Harmony.PatchAll(typeof(RotateShipDecorSelectionPatch)); + Harmony.PatchAll(typeof(SyncShipUnlockablesPatch)); + Harmony.PatchAll(typeof(TerminalItemSalesPatches)); + Harmony.PatchAll(typeof(TerminalScrollMousePatch)); + Harmony.PatchAll(typeof(UnlockShipObjectPatches)); + // ... StaticLogger.LogInfo($"{PLUGIN_NAME} loaded!"); } diff --git a/StoreRotationConfig/StoreRotationConfig.csproj b/StoreRotationConfig/StoreRotationConfig.csproj index fc1ddf0..48e7b5b 100644 --- a/StoreRotationConfig/StoreRotationConfig.csproj +++ b/StoreRotationConfig/StoreRotationConfig.csproj @@ -3,7 +3,7 @@ StoreRotationConfig Configure the number of items in each store rotation, show them all, remove purchases, sort them, and/or enable sales for them. - 2.3.1 + 2.3.2 @@ -46,6 +46,10 @@ managed\Unity.Netcode.Runtime.dll + + + managed\TerminalFormatter.dll + diff --git a/manifest.json b/manifest.json index 2ccb620..b3c09d2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "name": "StoreRotationConfig", - "version_number": "2.3.1", + "version_number": "2.3.2", "website_url": "https://github.com/pacoito123/LC_StoreRotationConfig", "description": "Configure the number of items in each store rotation, show them all, remove purchases, sort them, and/or enable sales for them. Also includes a fix for the terminal scrolling too far and skipping lines.", "dependencies": [