diff --git a/ExtraProperties.cs b/ExtraProperties.cs index 9c58ec5..3fa7806 100644 --- a/ExtraProperties.cs +++ b/ExtraProperties.cs @@ -1,5 +1,7 @@ using EFT.InventoryLogic; using EFT.UI.DragAndDrop; +using EFT.UI.Ragfair; +using System; using System.Runtime.CompilerServices; using UnityEngine; @@ -87,3 +89,16 @@ public static class ExtraItemViewStatsProperties public static void SetHideMods(this ItemViewStats itemViewStats, bool value) => properties.GetOrCreateValue(itemViewStats).HideMods = value; } +public static class ExtraItemMarketPricesPanelProperties +{ + private static readonly ConditionalWeakTable properties = new(); + + private class Properties + { + public Action OnMarketPricesCallback = null; + } + + public static Action GetOnMarketPricesCallback(this ItemMarketPricesPanel panel) => properties.GetOrCreateValue(panel).OnMarketPricesCallback; + public static Action SetOnMarketPricesCallback(this ItemMarketPricesPanel panel, Action handler) => properties.GetOrCreateValue(panel).OnMarketPricesCallback = handler; +} + diff --git a/Patches/AddOfferClickablePricesPatches.cs b/Patches/AddOfferClickablePricesPatches.cs index f4ab190..40271e6 100644 --- a/Patches/AddOfferClickablePricesPatches.cs +++ b/Patches/AddOfferClickablePricesPatches.cs @@ -1,7 +1,9 @@ -using EFT.UI.Ragfair; +using EFT.InventoryLogic; +using EFT.UI.Ragfair; using HarmonyLib; using JetBrains.Annotations; using SPT.Reflection.Patching; +using System; using System.Linq; using System.Reflection; using TMPro; @@ -16,6 +18,9 @@ public static class AddOfferClickablePricesPatches public static void Enable() { new AddButtonPatch().Enable(); + new MarketPriceUpdatePatch().Enable(); + new BulkTogglePatch().Enable(); + new MultipleStacksPatch().Enable(); } public class AddButtonPatch : ModulePatch @@ -43,10 +48,99 @@ public static class AddOfferClickablePricesPatches Button maximumButton = panel.MaximumLabel.GetOrAddComponent(); maximumButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Maximum)); ____pricesPanel.AddDisposable(maximumButton.onClick.RemoveAllListeners); + + ____pricesPanel.SetOnMarketPricesCallback(() => PopulateOfferPrice(__instance, ____pricesPanel, rublesRequirement)); } } - private static void SetRequirement(AddOfferWindow window, RequirementView requirement, float price) + public class MarketPriceUpdatePatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(ItemMarketPricesPanel), nameof(ItemMarketPricesPanel.method_1)); + } + + [PatchPostfix] + public static void Postfix(ItemMarketPricesPanel __instance) + { + var action = __instance.GetOnMarketPricesCallback(); + action?.Invoke(); + } + } + + public class BulkTogglePatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_12)); + } + + [PatchPostfix] + public static void Postfix(AddOfferWindow __instance, bool arg, ItemMarketPricesPanel ____pricesPanel, RequirementView[] ____requirementViews) + { + if (!Settings.UpdatePriceOnBulk.Value) + { + return; + } + + RequirementView rublesRequirement = ____requirementViews.First(rv => rv.name == "Requirement (RUB)"); + double currentPrice = rublesRequirement.Requirement.PreciseCount; + if (currentPrice <= 0) + { + return; + } + + // SetRequirement will multiply (or not), so just need the individual price + double individualPrice = arg ? currentPrice : Math.Ceiling(currentPrice / __instance.Int32_0); + SetRequirement(__instance, rublesRequirement, individualPrice); + } + } + + // Called when item selection changes. Handles updating price if bulk is (or was) checked + public class MultipleStacksPatch : ModulePatch + { + private static bool WasBulk; + + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_9)); + } + + [PatchPrefix] + public static void Prefix(AddOfferWindow __instance) + { + WasBulk = __instance.R().BulkOffer; + } + + [PatchPostfix] + public static void Postfix(AddOfferWindow __instance, Item item, bool selected, ItemMarketPricesPanel ____pricesPanel, RequirementView[] ____requirementViews) + { + if (!Settings.UpdatePriceOnBulk.Value || __instance.Int32_0 < 1) + { + return; + } + + // Bulk can autochange when selecting/deselecting, so only bail if it wasn't and still isn't bulk + if (!WasBulk && !__instance.R().BulkOffer) + { + return; + } + + var rublesRequirement = ____requirementViews.First(rv => rv.name == "Requirement (RUB)"); + double currentPrice = rublesRequirement.Requirement.PreciseCount; + + // Need to figure out the price per item *before* this item was added/removed + int oldCount = __instance.Int32_0 + (selected ? -item.StackObjectsCount : item.StackObjectsCount); + if (oldCount <= 0) + { + return; + } + + SetRequirement(__instance, rublesRequirement, currentPrice / oldCount); + } + } + + private static void SetRequirement(AddOfferWindow window, RequirementView requirement, double price) { if (window.R().BulkOffer) { @@ -56,6 +150,26 @@ public static class AddOfferClickablePricesPatches requirement.method_0(price.ToString("F0")); } + private static void PopulateOfferPrice(AddOfferWindow window, ItemMarketPricesPanel pricesPanel, RequirementView rublesRequirement) + { + switch (Settings.AutoOfferPrice.Value) + { + case AutoFleaPrice.Minimum: + SetRequirement(window, rublesRequirement, pricesPanel.Minimum); + break; + case AutoFleaPrice.Average: + SetRequirement(window, rublesRequirement, pricesPanel.Average); + break; + case AutoFleaPrice.Maximum: + SetRequirement(window, rublesRequirement, pricesPanel.Maximum); + break; + case AutoFleaPrice.None: + default: + break; + } + } + + public class HighlightButton : Button { private Color originalColor; diff --git a/Patches/UnloadAmmoPatches.cs b/Patches/UnloadAmmoPatches.cs index 118b798..19089b2 100644 --- a/Patches/UnloadAmmoPatches.cs +++ b/Patches/UnloadAmmoPatches.cs @@ -1,4 +1,5 @@ using Comfort.Common; +using EFT; using EFT.InventoryLogic; using EFT.UI; using HarmonyLib; @@ -115,15 +116,21 @@ public static class UnloadAmmoPatches [PatchPrefix] public static void Prefix(Item item) { - if (item is AmmoBox) + if (Settings.UnloadAmmoBoxInPlace.Value && item is AmmoBox) { UnloadState = new(); } } [PatchPostfix] - public static void Postfix() + public static async void Postfix(Task __result) { + if (!Settings.UnloadAmmoBoxInPlace.Value) + { + return; + } + + await __result; UnloadState = null; } } @@ -143,8 +150,7 @@ public static class UnloadAmmoPatches return; } - AmmoBox box = item.Parent.Container.ParentItem as AmmoBox; - if (box == null) + if (item.Parent.Container.ParentItem is not AmmoBox box) { return; } @@ -191,10 +197,17 @@ public static class UnloadAmmoPatches public UnloadAmmoBoxState() { - fakeStash = (StashClass)Singleton.Instance.CreateItem("FakeStash", "566abbc34bdc2d92178b4576", null); + if (Plugin.InRaid()) + { + fakeStash = Singleton.Instance.R().Stash; + } + else + { + fakeStash = (StashClass)Singleton.Instance.CreateItem("FakeStash", "566abbc34bdc2d92178b4576", null); - var profile = PatchConstants.BackEndSession.Profile; - fakeController = new(fakeStash, profile.ProfileId, profile.Nickname); + var profile = PatchConstants.BackEndSession.Profile; + fakeController = new(fakeStash, profile.ProfileId, profile.Nickname); + } } } } diff --git a/R.cs b/R.cs index 6ea5f3d..b9c8aaf 100644 --- a/R.cs +++ b/R.cs @@ -67,6 +67,7 @@ public static class R InventoryScreen.InitTypes(); ScavengerInventoryScreen.InitTypes(); LocalizedText.InitTypes(); + GameWorld.InitTypes(); } public abstract class Wrapper(object value) @@ -867,6 +868,20 @@ public static class R set { StringCaseField.SetValue(Value, value); } } } + + public class GameWorld(object value) : Wrapper(value) + { + public static Type Type { get; private set; } + private static FieldInfo StashField; + + public static void InitTypes() + { + Type = typeof(EFT.GameWorld); + StashField = AccessTools.Field(Type, "stashClass"); + } + + public StashClass Stash { get { return (StashClass)StashField.GetValue(Value); } } + } } public static class RExtentensions @@ -898,4 +913,5 @@ public static class RExtentensions public static R.InventoryScreen R(this InventoryScreen value) => new(value); public static R.ScavengerInventoryScreen R(this ScavengerInventoryScreen value) => new(value); public static R.LocalizedText R(this LocalizedText value) => new(value); + public static R.GameWorld R(this GameWorld value) => new(value); } diff --git a/Settings.cs b/Settings.cs index 7d1cc59..57863a2 100644 --- a/Settings.cs +++ b/Settings.cs @@ -37,6 +37,14 @@ internal enum SortingTableDisplay Both } +internal enum AutoFleaPrice +{ + None, + Minimum, + Average, + Maximum +} + internal class Settings { // Categories @@ -87,6 +95,7 @@ internal class Settings public static ConfigEntry SwapItems { get; set; } public static ConfigEntry SwapMags { get; set; } public static ConfigEntry AlwaysSwapMags { get; set; } + public static ConfigEntry UnloadAmmoBoxInPlace { get; set; } // Advanced public static ConfigEntry SwapImpossibleContainers { get; set; } public static ConfigEntry ReorderGrids { get; set; } public static ConfigEntry SynchronizeStashScrolling { get; set; } @@ -125,6 +134,8 @@ internal class Settings public static ConfigEntry ShowRequiredQuest { get; set; } public static ConfigEntry AutoExpandCategories { get; set; } public static ConfigEntry ClearFiltersOnSearch { get; set; } + public static ConfigEntry AutoOfferPrice { get; set; } + public static ConfigEntry UpdatePriceOnBulk { get; set; } public static ConfigEntry KeepAddOfferOpen { get; set; } public static ConfigEntry PurchaseAllKeybind { get; set; } public static ConfigEntry KeepAddOfferOpenIgnoreMaxOffers { get; set; } // Advanced @@ -452,6 +463,15 @@ internal class Settings null, new ConfigurationManagerAttributes { }))); + configEntries.Add(UnloadAmmoBoxInPlace = config.Bind( + InventorySection, + "Unload Ammo Boxes In-Place", + true, + new ConfigDescription( + "Whether to unload ammo boxes in-place, otherwise there needs to be free space somewhere", + null, + new ConfigurationManagerAttributes { IsAdvanced = true }))); + configEntries.Add(SwapImpossibleContainers = config.Bind( InventorySection, "Swap with Incompatible Containers", @@ -734,6 +754,24 @@ internal class Settings null, new ConfigurationManagerAttributes { }))); + configEntries.Add(AutoOfferPrice = config.Bind( + FleaMarketSection, + "Autopopulate Offer Price", + AutoFleaPrice.None, + new ConfigDescription( + "Autopopulte new offers with min/avg/max market price, or leave blank", + null, + new ConfigurationManagerAttributes { }))); + + configEntries.Add(UpdatePriceOnBulk = config.Bind( + FleaMarketSection, + "Update Offer Price on Bulk", + true, + new ConfigDescription( + "Automatically multiply or divide the price when you check/uncheck bulk, or or when you change the number of selected items while bulk is checked.", + null, + new ConfigurationManagerAttributes { }))); + configEntries.Add(ShowRequiredQuest = config.Bind( FleaMarketSection, "Show Required Quest for Locked Offers", diff --git a/UIFixes.csproj b/UIFixes.csproj index fd3a371..69bc19d 100644 --- a/UIFixes.csproj +++ b/UIFixes.csproj @@ -4,7 +4,7 @@ net471 Tyfon.UIFixes SPT UI Fixes - 2.3.1 + 2.3.2 true latest Debug;Release diff --git a/server/package.json b/server/package.json index c5a5469..68db11c 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "uifixes", - "version": "2.3.1", + "version": "2.3.2", "main": "src/mod.js", "license": "MIT", "author": "Tyfon",