diff --git a/GlobalSuppressions.cs b/GlobalSuppressions.cs new file mode 100644 index 0000000..63fa6a5 --- /dev/null +++ b/GlobalSuppressions.cs @@ -0,0 +1,8 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE0028:Simplify collection initialization", Justification = "", Scope = "member", Target = "~M:UIFixes.Settings.Init(BepInEx.Configuration.ConfigFile)")] diff --git a/InsuranceInteractions.cs b/InsuranceInteractions.cs new file mode 100644 index 0000000..af2c535 --- /dev/null +++ b/InsuranceInteractions.cs @@ -0,0 +1,104 @@ +using Comfort.Common; +using EFT.InventoryLogic; +using EFT.UI; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static RootMotion.FinalIK.InteractionTrigger.Range; + +namespace UIFixes +{ + public class InsuranceInteractions(Item item, ItemUiContext uiContext, int playerRubles) : ItemInfoInteractionsAbstractClass(uiContext) + { + private readonly InsuranceCompanyClass insurance = uiContext.Session.InsuranceCompany; + private readonly Item item = item; + private readonly int playerRubles = playerRubles; + private List items; + private readonly Dictionary prices = []; + + public void LoadAsync(Action callback) + { + ItemClass itemClass = ItemClass.FindOrCreate(item); + items = insurance.GetItemChildren(itemClass).Flatten(insurance.GetItemChildren).Concat([itemClass]) + .Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i)) + .ToList(); + + insurance.GetInsurePriceAsync(items, _ => + { + foreach (var insurer in insurance.Insurers) + { + int price = this.items.Select(i => insurance.InsureSummary[insurer.Id][i]).Where(s => s.Loaded).Sum(s => s.Amount); + prices[insurer.Id] = price; + + string priceColor = price > playerRubles ? "#FF0000" : "#ADB8BC"; + + string text = string.Format("{0} ({2} ₽)", insurer.LocalizedName, priceColor, price); + + base.method_2(MakeInteractionId(insurer.Id), text, () => this.Insure(insurer.Id)); + } + + callback(); + }); + } + + private void Insure(string insurerId) + { + insurance.SelectedInsurerId = insurerId; + insurance.InsureItems(this.items, result => { }); + } + + public IResult GetButtonInteraction(string interactionId) + { + string traderId = interactionId.Split(':')[1]; + if (prices[traderId] > playerRubles) + { + return new FailedResult("ragfair/Not enough money", 0); + } + + return SuccessfulResult.New; + } + + public override void ExecuteInteractionInternal(EInsurers interaction) + { + } + + public override bool IsActive(EInsurers button) + { + return button == EInsurers.None && !this.insurance.Insurers.Any(); + } + + public override IResult IsInteractive(EInsurers button) + { + return new FailedResult("No insurers??", 0); + } + + public override bool HasIcons + { + get { return false; } + } + + public enum EInsurers + { + None + } + private static string MakeInteractionId(string traderId) + { + return "UIFixesInsurerId:" + traderId; + } + + public static bool IsInsuranceInteractionId(string id) + { + return id.StartsWith("UIFixesInsurerId:"); + } + } + + public static class InsuranceExtensions + { + public static bool IsInsuranceInteraction(this DynamicInteractionClass interaction) + { + return interaction.Id.StartsWith("UIFixesInsurerId:"); + } + } +} diff --git a/Patches/FleaPrevSearchPatches.cs b/Patches/FleaPrevSearchPatches.cs index 931c68c..4eb4297 100644 --- a/Patches/FleaPrevSearchPatches.cs +++ b/Patches/FleaPrevSearchPatches.cs @@ -242,27 +242,14 @@ namespace UIFixes private static void UpdateColumnHeaders(FiltersPanel filtersPanel, ESortType sortType, bool sortDirection) { var wrappedFiltersPanel = filtersPanel.R(); - RagfairFilterButton button; - switch (sortType) + RagfairFilterButton button = sortType switch { - case ESortType.Barter: - button = wrappedFiltersPanel.BarterButton; - break; - case ESortType.Rating: - button = wrappedFiltersPanel.RatingButton; - break; - case ESortType.OfferItem: - button = wrappedFiltersPanel.OfferItemButton; - break; - case ESortType.ExpirationDate: - button = wrappedFiltersPanel.ExpirationButton; - break; - case ESortType.Price: - default: // Default to price if somehow this falls through - button = wrappedFiltersPanel.PriceButton; - break; - } - + ESortType.Barter => wrappedFiltersPanel.BarterButton, + ESortType.Rating => wrappedFiltersPanel.RatingButton, + ESortType.OfferItem => wrappedFiltersPanel.OfferItemButton, + ESortType.ExpirationDate => wrappedFiltersPanel.ExpirationButton, + _ => wrappedFiltersPanel.PriceButton, + }; wrappedFiltersPanel.SortDescending = sortDirection; filtersPanel.method_4(button); } diff --git a/Patches/InsureContextMenuPatches.cs b/Patches/InsureContextMenuPatches.cs index 4ad0e39..c9ffa0d 100644 --- a/Patches/InsureContextMenuPatches.cs +++ b/Patches/InsureContextMenuPatches.cs @@ -17,10 +17,9 @@ namespace UIFixes private static Type TradingRootInteractionsType; private static FieldInfo TradingRootInteractionsItemField; - private static int PlayerRubles; - private static InsuranceInteractions CurrentInsuranceInteractions = null; - private static string CreatedContextMenuButtonTraderId = null; + private static RepairInteractions CurrentRepairInteractions = null; + private static string CreatedButtonInteractionId = null; private static readonly HashSet TradingRootInteractions = [ @@ -84,7 +83,7 @@ namespace UIFixes [PatchPostfix] public static void Postfix(ref IEnumerable __result) { - __result = __result.Append(EItemInfoButton.Insure); + __result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure); } } @@ -98,17 +97,25 @@ namespace UIFixes [PatchPrefix] public static bool Prefix(EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, Item ___item_0, ItemUiContext ___itemUiContext_1) { + Dictionary playerCurrencies = R.Money.GetMoneySums(___itemUiContext_1.R().InventoryController.Inventory.Stash.Grid.ContainedItems.Keys); + int playerRubles = playerCurrencies[ECurrencyType.RUB]; + if (parentInteraction == EItemInfoButton.Insure) { - Dictionary playerCurrencies = R.Money.GetMoneySums(___itemUiContext_1.R().InventoryController.Inventory.Stash.Grid.ContainedItems.Keys); - PlayerRubles = playerCurrencies[ECurrencyType.RUB]; - - CurrentInsuranceInteractions = new(___item_0, ___itemUiContext_1); + CurrentInsuranceInteractions = new(___item_0, ___itemUiContext_1, playerRubles); CurrentInsuranceInteractions.LoadAsync(() => subInteractionsWrapper.SetSubInteractions(CurrentInsuranceInteractions)); return false; } + if (parentInteraction == EItemInfoButton.Repair) + { + CurrentRepairInteractions = new(___item_0, ___itemUiContext_1, playerRubles); + subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions); + + return false; + } + return true; } } @@ -123,7 +130,7 @@ namespace UIFixes [PatchPostfix] public static void Postfix(ref IEnumerable __result) { - __result = __result.Append(EItemInfoButton.Insure); + __result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure); } } @@ -137,20 +144,28 @@ namespace UIFixes [PatchPrefix] public static bool Prefix(object __instance, EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, ItemUiContext ___itemUiContext_0) { + Dictionary playerCurrencies = R.Money.GetMoneySums(___itemUiContext_0.R().InventoryController.Inventory.Stash.Grid.ContainedItems.Keys); + int playerRubles = playerCurrencies[ECurrencyType.RUB]; + + // CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3032 + Item item = (Item)TradingRootInteractionsItemField.GetValue(__instance); + if (parentInteraction == EItemInfoButton.Insure) { - Dictionary playerCurrencies = R.Money.GetMoneySums(___itemUiContext_0.R().InventoryController.Inventory.Stash.Grid.ContainedItems.Keys); - PlayerRubles = playerCurrencies[ECurrencyType.RUB]; - - // CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3032 - Item item = (Item)TradingRootInteractionsItemField.GetValue(__instance); - - CurrentInsuranceInteractions = new(item, ___itemUiContext_0); + CurrentInsuranceInteractions = new(item, ___itemUiContext_0, playerRubles); CurrentInsuranceInteractions.LoadAsync(() => subInteractionsWrapper.SetSubInteractions(CurrentInsuranceInteractions)); return false; } + if (parentInteraction == EItemInfoButton.Repair) + { + CurrentRepairInteractions = new(item, ___itemUiContext_0, playerRubles); + subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions); + + return false; + } + return true; } } @@ -165,16 +180,16 @@ namespace UIFixes [PatchPrefix] public static void Prefix(DynamicInteractionClass interaction) { - if (interaction.IsInsuranceInteraction()) + if (interaction.IsInsuranceInteraction() || interaction.IsRepairInteraction()) { - CreatedContextMenuButtonTraderId = interaction.GetTraderId(); + CreatedButtonInteractionId = interaction.Id; } } [PatchPostfix] public static void Postfix() { - CreatedContextMenuButtonTraderId = null; + CreatedButtonInteractionId = null; } } @@ -188,9 +203,16 @@ namespace UIFixes [PatchPrefix] public static void Prefix(SimpleContextMenuButton button) { - if (!String.IsNullOrEmpty(CreatedContextMenuButtonTraderId) && CurrentInsuranceInteractions != null) + if (!String.IsNullOrEmpty(CreatedButtonInteractionId)) { - button.SetButtonInteraction(CurrentInsuranceInteractions.GetButtonInteraction(CreatedContextMenuButtonTraderId)); + if (InsuranceInteractions.IsInsuranceInteractionId(CreatedButtonInteractionId) && CurrentInsuranceInteractions != null) + { + button.SetButtonInteraction(CurrentInsuranceInteractions.GetButtonInteraction(CreatedButtonInteractionId)); + } + else if (RepairInteractions.IsRepairInteractionId(CreatedButtonInteractionId) && CurrentRepairInteractions != null) + { + button.SetButtonInteraction(CurrentRepairInteractions.GetButtonInteraction(CreatedButtonInteractionId)); + } } } } @@ -208,93 +230,5 @@ namespace UIFixes CurrentInsuranceInteractions = null; } } - - public class InsuranceInteractions(Item item, ItemUiContext uiContext) : ItemInfoInteractionsAbstractClass(uiContext) - { - private readonly InsuranceCompanyClass insurance = uiContext.Session.InsuranceCompany; - private readonly Item item = item; - private List items; - private readonly Dictionary prices = []; - - public void LoadAsync(Action callback) - { - ItemClass itemClass = ItemClass.FindOrCreate(item); - items = insurance.GetItemChildren(itemClass).Flatten(insurance.GetItemChildren).Concat([itemClass]) - .Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i)) - .ToList(); - - insurance.GetInsurePriceAsync(items, _ => - { - foreach (var insurer in insurance.Insurers) - { - int price = this.items.Select(i => insurance.InsureSummary[insurer.Id][i]).Where(s => s.Loaded).Sum(s => s.Amount); - prices[insurer.Id] = price; - - string priceColor = price > PlayerRubles ? "#FF0000" : "#ADB8BC"; - - string text = string.Format("{0} ({2} ₽)", insurer.LocalizedName, priceColor, price); - - base.method_2(MakeInteractionId(insurer.Id), text, () => this.Insure(insurer.Id)); - } - - callback(); - }); - } - - public void Insure(string insurerId) - { - insurance.SelectedInsurerId = insurerId; - insurance.InsureItems(this.items, result => { }); - } - - public IResult GetButtonInteraction(string traderId) - { - if (prices[traderId] > PlayerRubles) - { - return new FailedResult("ragfair/Not enough money", 0); - } - - return SuccessfulResult.New; - } - - public override void ExecuteInteractionInternal(EInsurers interaction) - { - } - - public override bool IsActive(EInsurers button) - { - return button == EInsurers.None && !this.insurance.Insurers.Any(); - } - - public override IResult IsInteractive(EInsurers button) - { - return new FailedResult("No insurers??", 0); - } - - public override bool HasIcons - { - get { return false; } - } - - public enum EInsurers - { - None - } - } - - private static string MakeInteractionId(string traderId) - { - return "UIFixesInsurerId:" + traderId; - } - - private static bool IsInsuranceInteraction(this DynamicInteractionClass interaction) - { - return interaction.Id.StartsWith("UIFixesInsurerId:"); - } - - private static string GetTraderId(this DynamicInteractionClass interaction) - { - return interaction.Id.Split(':')[1]; - } } } diff --git a/RepairInteractions.cs b/RepairInteractions.cs new file mode 100644 index 0000000..7e040b3 --- /dev/null +++ b/RepairInteractions.cs @@ -0,0 +1,158 @@ +using Comfort.Common; +using EFT.Communications; +using EFT.InventoryLogic; +using EFT.UI; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace UIFixes +{ + public class RepairInteractions : ItemInfoInteractionsAbstractClass + { + private readonly RepairControllerClass repairController; + private readonly int playerRubles; + private readonly GInterface33 repairStrategy; + + public RepairInteractions(Item item, ItemUiContext uiContext, int playerRubles) : base(uiContext) + { + repairController = uiContext.Session.RepairController; + + // Add empty action because otherwise RepairControllerClass.action_0 is null and it pukes on successful repair + repairController.OnSuccessfulRepairChangedEvent += () => {}; + + this.playerRubles = playerRubles; + + repairStrategy = item.GetItemComponent() != null ? + new GClass804(item, repairController) : + new GClass803(item, repairController); + + Load(); + } + + private void Load() + { + foreach (IRepairer repairer in repairStrategy.Repairers) + { + repairStrategy.CurrentRepairer = repairer; + float repairAmount = repairStrategy.HowMuchRepairScoresCanAccept(); + + string text; + if (repairer is GClass802 repairKit) + { + float pointsLeft = repairKit.GetRepairPoints(); + double amount = repairStrategy.GetRepairPrice(repairAmount, repairKit); + + string costColor = amount > pointsLeft ? "#FF0000" : "#ADB8BC"; + text = string.Format("{0} ({2} {3})", repairer.LocalizedName, costColor, Math.Round(amount, 2).ToString(CultureInfo.InvariantCulture), "RP".Localized()); + } + else + { + int price = repairStrategy.GetCurrencyPrice(repairAmount); + + string priceColor = price > playerRubles ? "#FF0000" : "#ADB8BC"; + text = string.Format("{0} ({2} ₽)", repairer.LocalizedName, priceColor, price); + } + + base.method_2(MakeInteractionId(repairer.RepairerId), text, () => this.Repair(repairer.RepairerId)); + } + } + + private async void Repair(string repairerId) + { + repairStrategy.CurrentRepairer = repairStrategy.Repairers.Single(r => r.RepairerId == repairerId); + IResult result = await repairStrategy.RepairItem(repairStrategy.HowMuchRepairScoresCanAccept(), null); + if (result.Succeed) + { + Singleton.Instance.PlayUISound(EUISoundType.RepairComplete); + NotificationManagerClass.DisplayMessageNotification(string.Format("{0} {1:F1}", "Item successfully repaired to".Localized(null), repairStrategy.Durability()), ENotificationDurationType.Default, ENotificationIconType.Default, null); + } + } + + public IResult GetButtonInteraction(string interactionId) + { + string repairerId = interactionId.Split(':')[1]; + IRepairer repairer = repairStrategy.Repairers.Single(r => r.RepairerId == repairerId); + repairStrategy.CurrentRepairer = repairer; + + float repairAmount = repairStrategy.HowMuchRepairScoresCanAccept(); + + if (repairer is GClass802 repairKit) + { + float pointsLeft = repairKit.GetRepairPoints(); + double amount = repairStrategy.GetRepairPrice(repairAmount, repairKit); + if (amount > pointsLeft) + { + return new FailedResult(ERepairStatusWarning.NotEnoughRepairPoints.ToString()); + } + } + else + { + int price = repairStrategy.GetCurrencyPrice(repairAmount); + if (price > playerRubles) + { + return new FailedResult(ERepairStatusWarning.NotEnoughMoney.ToString()); + } + } + + if (repairAmount < float.Epsilon) + { + return new FailedResult(ERepairStatusWarning.NothingToRepair.ToString()); + } + + if (repairStrategy.BrokenItemError()) + { + return new FailedResult(ERepairStatusWarning.BrokenItem.ToString()); + } + + if (repairStrategy.IsNoCorrespondingArea()) + { + return new FailedResult(ERepairStatusWarning.NoCorrespondingArea.ToString()); + } + + return SuccessfulResult.New; + } + + public override void ExecuteInteractionInternal(ERepairers interaction) + { + } + + public override bool IsActive(ERepairers button) + { + return button == ERepairers.None && !this.repairController.TraderRepairers.Any(); + } + + public override IResult IsInteractive(ERepairers button) + { + return new FailedResult("No repairers??", 0); + } + + public override bool HasIcons + { + get { return false; } + } + + public enum ERepairers + { + None + } + private static string MakeInteractionId(string traderId) + { + return "UIFixesRepairerId:" + traderId; + } + + public static bool IsRepairInteractionId(string id) + { + return id.StartsWith("UIFixesRepairerId:"); + } + } + + public static class RepairExtensions + { + public static bool IsRepairInteraction(this DynamicInteractionClass interaction) + { + return interaction.Id.StartsWith("UIFixesRepairerId:"); + } + } +}