using file-scoped namespaces
This commit is contained in:
@@ -4,26 +4,25 @@ using EFT.UI.Ragfair;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace UIFixes.ContextMenus
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class EmptySlotMenu(Slot slot, ItemContextAbstractClass itemContext, ItemUiContext itemUiContext, Action closeAction) : BaseItemInfoInteractions(itemContext, itemUiContext, closeAction)
|
||||||
{
|
{
|
||||||
public class EmptySlotMenu(Slot slot, ItemContextAbstractClass itemContext, ItemUiContext itemUiContext, Action closeAction) : BaseItemInfoInteractions(itemContext, itemUiContext, closeAction)
|
private static readonly List<EItemInfoButton> Actions = [EItemInfoButton.LinkedSearch];
|
||||||
|
|
||||||
|
private readonly Slot slot = slot;
|
||||||
|
|
||||||
|
public override IEnumerable<EItemInfoButton> AvailableInteractions => Actions;
|
||||||
|
|
||||||
|
public override void ExecuteInteractionInternal(EItemInfoButton interaction)
|
||||||
{
|
{
|
||||||
private static readonly List<EItemInfoButton> Actions = [EItemInfoButton.LinkedSearch];
|
switch (interaction)
|
||||||
|
|
||||||
private readonly Slot slot = slot;
|
|
||||||
|
|
||||||
public override IEnumerable<EItemInfoButton> AvailableInteractions => Actions;
|
|
||||||
|
|
||||||
public override void ExecuteInteractionInternal(EItemInfoButton interaction)
|
|
||||||
{
|
{
|
||||||
switch (interaction)
|
case EItemInfoButton.LinkedSearch:
|
||||||
{
|
Search(new(EFilterType.LinkedSearch, slot.ParentItem.TemplateId + ":" + slot.Id, true));
|
||||||
case EItemInfoButton.LinkedSearch:
|
break;
|
||||||
Search(new(EFilterType.LinkedSearch, slot.ParentItem.TemplateId + ":" + slot.Id, true));
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,79 +4,78 @@ using System;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
namespace UIFixes.ContextMenus
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class EmptySlotMenuTrigger : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
|
||||||
{
|
{
|
||||||
public class EmptySlotMenuTrigger : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler
|
private ItemUiContext itemUiContext;
|
||||||
|
private Slot slot;
|
||||||
|
private ItemContextAbstractClass parentContext;
|
||||||
|
private bool hovered = false;
|
||||||
|
|
||||||
|
public void Init(Slot slot, ItemContextAbstractClass parentContext, ItemUiContext itemUiContext)
|
||||||
{
|
{
|
||||||
private ItemUiContext itemUiContext;
|
this.itemUiContext = itemUiContext;
|
||||||
private Slot slot;
|
this.slot = slot;
|
||||||
private ItemContextAbstractClass parentContext;
|
this.parentContext = parentContext;
|
||||||
private bool hovered = false;
|
|
||||||
|
|
||||||
public void Init(Slot slot, ItemContextAbstractClass parentContext, ItemUiContext itemUiContext)
|
|
||||||
{
|
|
||||||
this.itemUiContext = itemUiContext;
|
|
||||||
this.slot = slot;
|
|
||||||
this.parentContext = parentContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (!hovered)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.LinkedSearchKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
using EmptySlotContext context = new(slot, parentContext, itemUiContext);
|
|
||||||
var interactions = itemUiContext.GetItemContextInteractions(context, null);
|
|
||||||
interactions.ExecuteInteraction(EItemInfoButton.LinkedSearch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerClick(PointerEventData eventData)
|
|
||||||
{
|
|
||||||
if (eventData.button == PointerEventData.InputButton.Right)
|
|
||||||
{
|
|
||||||
EmptySlotContext context = new(slot, parentContext, itemUiContext);
|
|
||||||
itemUiContext.ShowContextMenu(context, eventData.position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerDown(PointerEventData eventData) { }
|
|
||||||
|
|
||||||
public void OnPointerEnter(PointerEventData eventData)
|
|
||||||
{
|
|
||||||
hovered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerExit(PointerEventData eventData)
|
|
||||||
{
|
|
||||||
hovered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPointerUp(PointerEventData eventData) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EmptySlotContext(Slot slot, ItemContextAbstractClass parentContext, ItemUiContext itemUiContext) : ItemContextAbstractClass(parentContext.Item, parentContext.ViewType, parentContext)
|
public void Update()
|
||||||
{
|
{
|
||||||
private readonly Slot slot = slot;
|
if (!hovered)
|
||||||
private readonly ItemUiContext itemUiContext = itemUiContext;
|
|
||||||
|
|
||||||
public override ItemInfoInteractionsAbstractClass<EItemInfoButton> GetItemContextInteractions(Action closeAction)
|
|
||||||
{
|
{
|
||||||
return new EmptySlotMenu(slot, ItemContextAbstractClass, itemUiContext, () =>
|
return;
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
closeAction?.Invoke();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ItemContextAbstractClass CreateChild(Item item)
|
if (Settings.LinkedSearchKeyBind.Value.IsDown())
|
||||||
{
|
{
|
||||||
// Should never happen
|
using EmptySlotContext context = new(slot, parentContext, itemUiContext);
|
||||||
throw new NotImplementedException();
|
var interactions = itemUiContext.GetItemContextInteractions(context, null);
|
||||||
|
interactions.ExecuteInteraction(EItemInfoButton.LinkedSearch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnPointerClick(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
if (eventData.button == PointerEventData.InputButton.Right)
|
||||||
|
{
|
||||||
|
EmptySlotContext context = new(slot, parentContext, itemUiContext);
|
||||||
|
itemUiContext.ShowContextMenu(context, eventData.position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPointerDown(PointerEventData eventData) { }
|
||||||
|
|
||||||
|
public void OnPointerEnter(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
hovered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPointerExit(PointerEventData eventData)
|
||||||
|
{
|
||||||
|
hovered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPointerUp(PointerEventData eventData) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EmptySlotContext(Slot slot, ItemContextAbstractClass parentContext, ItemUiContext itemUiContext) : ItemContextAbstractClass(parentContext.Item, parentContext.ViewType, parentContext)
|
||||||
|
{
|
||||||
|
private readonly Slot slot = slot;
|
||||||
|
private readonly ItemUiContext itemUiContext = itemUiContext;
|
||||||
|
|
||||||
|
public override ItemInfoInteractionsAbstractClass<EItemInfoButton> GetItemContextInteractions(Action closeAction)
|
||||||
|
{
|
||||||
|
return new EmptySlotMenu(slot, ItemContextAbstractClass, itemUiContext, () =>
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
closeAction?.Invoke();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ItemContextAbstractClass CreateChild(Item item)
|
||||||
|
{
|
||||||
|
// Should never happen
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,101 +5,100 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class InsuranceInteractions(IEnumerable<Item> items, ItemUiContext uiContext, int playerRubles) : ItemInfoInteractionsAbstractClass<InsuranceInteractions.EInsurers>(uiContext)
|
||||||
{
|
{
|
||||||
public class InsuranceInteractions(IEnumerable<Item> items, ItemUiContext uiContext, int playerRubles) : ItemInfoInteractionsAbstractClass<InsuranceInteractions.EInsurers>(uiContext)
|
private readonly InsuranceCompanyClass insurance = uiContext.Session.InsuranceCompany;
|
||||||
|
private readonly List<Item> items = items.ToList();
|
||||||
|
private readonly int playerRubles = playerRubles;
|
||||||
|
private List<InsuranceItem> insurableItems;
|
||||||
|
private readonly Dictionary<string, int> prices = [];
|
||||||
|
|
||||||
|
public InsuranceInteractions(Item item, ItemUiContext uiContext, int playerRubles) : this([item], uiContext, playerRubles) { }
|
||||||
|
|
||||||
|
public void LoadAsync(Action callback)
|
||||||
{
|
{
|
||||||
private readonly InsuranceCompanyClass insurance = uiContext.Session.InsuranceCompany;
|
IEnumerable<InsuranceItem> InsuranceItemes = items.Select(InsuranceItem.FindOrCreate);
|
||||||
private readonly List<Item> items = items.ToList();
|
insurableItems = InsuranceItemes.SelectMany(insurance.GetItemChildren)
|
||||||
private readonly int playerRubles = playerRubles;
|
.Flatten(insurance.GetItemChildren)
|
||||||
private List<InsuranceItem> insurableItems;
|
.Concat(InsuranceItemes)
|
||||||
private readonly Dictionary<string, int> prices = [];
|
.Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
public InsuranceInteractions(Item item, ItemUiContext uiContext, int playerRubles) : this([item], uiContext, playerRubles) { }
|
insurance.GetInsurePriceAsync(insurableItems, _ =>
|
||||||
|
|
||||||
public void LoadAsync(Action callback)
|
|
||||||
{
|
{
|
||||||
IEnumerable<InsuranceItem> InsuranceItemes = items.Select(InsuranceItem.FindOrCreate);
|
foreach (var insurer in insurance.Insurers)
|
||||||
insurableItems = InsuranceItemes.SelectMany(insurance.GetItemChildren)
|
|
||||||
.Flatten(insurance.GetItemChildren)
|
|
||||||
.Concat(InsuranceItemes)
|
|
||||||
.Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
insurance.GetInsurePriceAsync(insurableItems, _ =>
|
|
||||||
{
|
{
|
||||||
foreach (var insurer in insurance.Insurers)
|
int price = this.insurableItems.Select(i => insurance.InsureSummary[insurer.Id][i]).Where(s => s.Loaded).Sum(s => s.Amount);
|
||||||
{
|
prices[insurer.Id] = price;
|
||||||
int price = this.insurableItems.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 priceColor = price > playerRubles ? "#FF0000" : "#ADB8BC";
|
||||||
|
|
||||||
string text = string.Format("<b><color=#C6C4B2>{0}</color> <color={1}>({2} ₽)</color></b>", insurer.LocalizedName, priceColor, price);
|
string text = string.Format("<b><color=#C6C4B2>{0}</color> <color={1}>({2} ₽)</color></b>", insurer.LocalizedName, priceColor, price);
|
||||||
|
|
||||||
base.method_2(MakeInteractionId(insurer.Id), text, () => this.Insure(insurer.Id));
|
base.method_2(MakeInteractionId(insurer.Id), text, () => this.Insure(insurer.Id));
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Insure(string insurerId)
|
|
||||||
{
|
|
||||||
insurance.SelectedInsurerId = insurerId;
|
|
||||||
insurance.InsureItems(this.insurableItems, 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;
|
callback();
|
||||||
}
|
});
|
||||||
|
|
||||||
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
|
private void Insure(string insurerId)
|
||||||
{
|
{
|
||||||
public static bool IsInsuranceInteraction(this DynamicInteractionClass interaction)
|
insurance.SelectedInsurerId = insurerId;
|
||||||
|
insurance.InsureItems(this.insurableItems, result => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
public IResult GetButtonInteraction(string interactionId)
|
||||||
|
{
|
||||||
|
string traderId = interactionId.Split(':')[1];
|
||||||
|
if (prices[traderId] > playerRubles)
|
||||||
{
|
{
|
||||||
return interaction.Id.StartsWith("UIFixesInsurerId:");
|
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:");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,176 +6,175 @@ using System;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class RepairInteractions : ItemInfoInteractionsAbstractClass<RepairInteractions.ERepairers>
|
||||||
{
|
{
|
||||||
public class RepairInteractions : ItemInfoInteractionsAbstractClass<RepairInteractions.ERepairers>
|
private readonly RepairControllerClass repairController;
|
||||||
|
private readonly int playerRubles;
|
||||||
|
private readonly R.RepairStrategy repairStrategy;
|
||||||
|
|
||||||
|
public RepairInteractions(Item item, ItemUiContext uiContext, int playerRubles) : base(uiContext)
|
||||||
{
|
{
|
||||||
private readonly RepairControllerClass repairController;
|
repairController = uiContext.Session.RepairController;
|
||||||
private readonly int playerRubles;
|
|
||||||
private readonly R.RepairStrategy repairStrategy;
|
|
||||||
|
|
||||||
public RepairInteractions(Item item, ItemUiContext uiContext, int playerRubles) : base(uiContext)
|
// Add empty action because otherwise RepairControllerClass.action_0 is null and it pukes on successful repair
|
||||||
|
repairController.OnSuccessfulRepairChangedEvent += () => { };
|
||||||
|
|
||||||
|
this.playerRubles = playerRubles;
|
||||||
|
|
||||||
|
repairStrategy = R.RepairStrategy.Create(item, repairController);
|
||||||
|
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Load()
|
||||||
|
{
|
||||||
|
foreach (IRepairer repairer in repairStrategy.Repairers)
|
||||||
{
|
{
|
||||||
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 = R.RepairStrategy.Create(item, repairController);
|
|
||||||
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Load()
|
|
||||||
{
|
|
||||||
foreach (IRepairer repairer in repairStrategy.Repairers)
|
|
||||||
{
|
|
||||||
repairStrategy.CurrentRepairer = repairer;
|
|
||||||
|
|
||||||
float repairAmount = GetClampedRepairAmount(repairStrategy);
|
|
||||||
|
|
||||||
string text;
|
|
||||||
if (repairAmount < float.Epsilon || !repairStrategy.CanRepair(repairStrategy.CurrentRepairer, repairStrategy.CurrentRepairer.Targets))
|
|
||||||
{
|
|
||||||
text = string.Format("<b><color=#C6C4B2>{0}</color></b>", repairer.LocalizedName);
|
|
||||||
}
|
|
||||||
else if (R.RepairKit.Type.IsInstanceOfType(repairer))
|
|
||||||
{
|
|
||||||
var repairKit = new R.RepairKit(repairer);
|
|
||||||
float pointsLeft = repairKit.GetRepairPoints();
|
|
||||||
double amount = repairStrategy.GetRepairPrice(repairAmount, repairKit.Value);
|
|
||||||
|
|
||||||
string costColor = amount > pointsLeft ? "#FF0000" : "#ADB8BC";
|
|
||||||
text = string.Format("<b><color=#C6C4B2>{0}</color> <color={1}>({2} {3})</color></b>", 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("<b><color=#C6C4B2>{0}</color> <color={1}>({2} ₽)</color></b>", repairer.LocalizedName, priceColor, price);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.method_2(MakeInteractionId(repairer.RepairerId), text, () => this.Repair(repairer.RepairerId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float GetClampedRepairAmount(R.RepairStrategy repairStrategy)
|
|
||||||
{
|
|
||||||
float repairAmount = repairStrategy.HowMuchRepairScoresCanAccept();
|
|
||||||
|
|
||||||
// The repair window round-trips this amount through a UI element that operatoes on percents, so it divides this by the max durability
|
|
||||||
// The UI element however has a minimum value of 0.001, which artificially caps how small a repair can be. To emulate this I have to do the same math
|
|
||||||
float percentAmount = repairAmount / repairStrategy.TemplateDurability();
|
|
||||||
|
|
||||||
return percentAmount < 0.001f ? 0 : repairAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<GUISounds>.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;
|
repairStrategy.CurrentRepairer = repairer;
|
||||||
|
|
||||||
if (!repairStrategy.CanRepair(repairStrategy.CurrentRepairer, repairStrategy.CurrentRepairer.Targets))
|
|
||||||
{
|
|
||||||
return new FailedResult(ERepairStatusWarning.ExceptionRepairItem.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
float repairAmount = GetClampedRepairAmount(repairStrategy);
|
float repairAmount = GetClampedRepairAmount(repairStrategy);
|
||||||
|
|
||||||
if (R.RepairKit.Type.IsInstanceOfType(repairer))
|
string text;
|
||||||
|
if (repairAmount < float.Epsilon || !repairStrategy.CanRepair(repairStrategy.CurrentRepairer, repairStrategy.CurrentRepairer.Targets))
|
||||||
|
{
|
||||||
|
text = string.Format("<b><color=#C6C4B2>{0}</color></b>", repairer.LocalizedName);
|
||||||
|
}
|
||||||
|
else if (R.RepairKit.Type.IsInstanceOfType(repairer))
|
||||||
{
|
{
|
||||||
var repairKit = new R.RepairKit(repairer);
|
var repairKit = new R.RepairKit(repairer);
|
||||||
float pointsLeft = repairKit.GetRepairPoints();
|
float pointsLeft = repairKit.GetRepairPoints();
|
||||||
double amount = repairStrategy.GetRepairPrice(repairAmount, repairKit.Value);
|
double amount = repairStrategy.GetRepairPrice(repairAmount, repairKit.Value);
|
||||||
if (amount > pointsLeft)
|
|
||||||
{
|
|
||||||
return new FailedResult(ERepairStatusWarning.NotEnoughRepairPoints.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// This check is only for repair kits
|
string costColor = amount > pointsLeft ? "#FF0000" : "#ADB8BC";
|
||||||
if (repairStrategy.IsNoCorrespondingArea())
|
text = string.Format("<b><color=#C6C4B2>{0}</color> <color={1}>({2} {3})</color></b>", repairer.LocalizedName, costColor, Math.Round(amount, 2).ToString(CultureInfo.InvariantCulture), "RP".Localized());
|
||||||
{
|
|
||||||
return new FailedResult(ERepairStatusWarning.NoCorrespondingArea.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int price = repairStrategy.GetCurrencyPrice(repairAmount);
|
int price = repairStrategy.GetCurrencyPrice(repairAmount);
|
||||||
if (price > playerRubles)
|
|
||||||
{
|
string priceColor = price > playerRubles ? "#FF0000" : "#ADB8BC";
|
||||||
return new FailedResult(ERepairStatusWarning.NotEnoughMoney.ToString());
|
text = string.Format("<b><color=#C6C4B2>{0}</color> <color={1}>({2} ₽)</color></b>", repairer.LocalizedName, priceColor, price);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repairAmount < float.Epsilon)
|
base.method_2(MakeInteractionId(repairer.RepairerId), text, () => this.Repair(repairer.RepairerId));
|
||||||
{
|
|
||||||
return new FailedResult(ERepairStatusWarning.NothingToRepair.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// BrokenItemError is not actually an error, they just implemented it that way - it shows a bunch of red text but it doesn't prevent repair
|
|
||||||
// Leaving this here to remember
|
|
||||||
/*if (repairStrategy.BrokenItemError())
|
|
||||||
{
|
|
||||||
return new FailedResult(ERepairStatusWarning.BrokenItem.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
|
private static float GetClampedRepairAmount(R.RepairStrategy repairStrategy)
|
||||||
{
|
{
|
||||||
public static bool IsRepairInteraction(this DynamicInteractionClass interaction)
|
float repairAmount = repairStrategy.HowMuchRepairScoresCanAccept();
|
||||||
|
|
||||||
|
// The repair window round-trips this amount through a UI element that operatoes on percents, so it divides this by the max durability
|
||||||
|
// The UI element however has a minimum value of 0.001, which artificially caps how small a repair can be. To emulate this I have to do the same math
|
||||||
|
float percentAmount = repairAmount / repairStrategy.TemplateDurability();
|
||||||
|
|
||||||
|
return percentAmount < 0.001f ? 0 : repairAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
return interaction.Id.StartsWith("UIFixesRepairerId:");
|
Singleton<GUISounds>.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;
|
||||||
|
|
||||||
|
if (!repairStrategy.CanRepair(repairStrategy.CurrentRepairer, repairStrategy.CurrentRepairer.Targets))
|
||||||
|
{
|
||||||
|
return new FailedResult(ERepairStatusWarning.ExceptionRepairItem.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
float repairAmount = GetClampedRepairAmount(repairStrategy);
|
||||||
|
|
||||||
|
if (R.RepairKit.Type.IsInstanceOfType(repairer))
|
||||||
|
{
|
||||||
|
var repairKit = new R.RepairKit(repairer);
|
||||||
|
float pointsLeft = repairKit.GetRepairPoints();
|
||||||
|
double amount = repairStrategy.GetRepairPrice(repairAmount, repairKit.Value);
|
||||||
|
if (amount > pointsLeft)
|
||||||
|
{
|
||||||
|
return new FailedResult(ERepairStatusWarning.NotEnoughRepairPoints.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// This check is only for repair kits
|
||||||
|
if (repairStrategy.IsNoCorrespondingArea())
|
||||||
|
{
|
||||||
|
return new FailedResult(ERepairStatusWarning.NoCorrespondingArea.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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// BrokenItemError is not actually an error, they just implemented it that way - it shows a bunch of red text but it doesn't prevent repair
|
||||||
|
// Leaving this here to remember
|
||||||
|
/*if (repairStrategy.BrokenItemError())
|
||||||
|
{
|
||||||
|
return new FailedResult(ERepairStatusWarning.BrokenItem.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:");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,30 @@
|
|||||||
using EFT.UI.DragAndDrop;
|
using EFT.UI.DragAndDrop;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class ExtraItemProperties
|
||||||
{
|
{
|
||||||
public static class ExtraItemProperties
|
private static readonly ConditionalWeakTable<Item, Properties> properties = new();
|
||||||
|
|
||||||
|
private class Properties
|
||||||
{
|
{
|
||||||
private static readonly ConditionalWeakTable<Item, Properties> properties = new();
|
public bool Reordered;
|
||||||
|
|
||||||
private class Properties
|
|
||||||
{
|
|
||||||
public bool Reordered;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool GetReordered(this Item item) => properties.GetOrCreateValue(item).Reordered;
|
|
||||||
public static void SetReordered(this Item item, bool value) => properties.GetOrCreateValue(item).Reordered = value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ExtraTemplatedGridsViewProperties
|
public static bool GetReordered(this Item item) => properties.GetOrCreateValue(item).Reordered;
|
||||||
{
|
public static void SetReordered(this Item item, bool value) => properties.GetOrCreateValue(item).Reordered = value;
|
||||||
private static readonly ConditionalWeakTable<TemplatedGridsView, Properties> properties = new();
|
}
|
||||||
|
|
||||||
private class Properties
|
public static class ExtraTemplatedGridsViewProperties
|
||||||
{
|
{
|
||||||
public bool Reordered;
|
private static readonly ConditionalWeakTable<TemplatedGridsView, Properties> properties = new();
|
||||||
}
|
|
||||||
|
private class Properties
|
||||||
public static bool GetReordered(this TemplatedGridsView gridsView) => properties.GetOrCreateValue(gridsView).Reordered;
|
{
|
||||||
public static void SetReordered(this TemplatedGridsView gridsView, bool value) => properties.GetOrCreateValue(gridsView).Reordered = value;
|
public bool Reordered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool GetReordered(this TemplatedGridsView gridsView) => properties.GetOrCreateValue(gridsView).Reordered;
|
||||||
|
public static void SetReordered(this TemplatedGridsView gridsView, bool value) => properties.GetOrCreateValue(gridsView).Reordered = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,236 +9,235 @@ using UnityEngine;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class DrawMultiSelect : MonoBehaviour
|
||||||
{
|
{
|
||||||
public class DrawMultiSelect : MonoBehaviour
|
private Texture2D selectTexture;
|
||||||
|
|
||||||
|
private Vector3 selectOrigin;
|
||||||
|
private Vector3 selectEnd;
|
||||||
|
|
||||||
|
private GraphicRaycaster localRaycaster;
|
||||||
|
private GraphicRaycaster preloaderRaycaster;
|
||||||
|
|
||||||
|
private bool drawing;
|
||||||
|
private bool secondary;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
{
|
{
|
||||||
private Texture2D selectTexture;
|
selectTexture = new Texture2D(1, 1);
|
||||||
|
selectTexture.SetPixel(0, 0, new Color(1f, 1f, 1f, 0.6f));
|
||||||
|
selectTexture.Apply();
|
||||||
|
|
||||||
private Vector3 selectOrigin;
|
localRaycaster = GetComponentInParent<GraphicRaycaster>();
|
||||||
private Vector3 selectEnd;
|
if (localRaycaster == null)
|
||||||
|
|
||||||
private GraphicRaycaster localRaycaster;
|
|
||||||
private GraphicRaycaster preloaderRaycaster;
|
|
||||||
|
|
||||||
private bool drawing;
|
|
||||||
private bool secondary;
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
{
|
||||||
selectTexture = new Texture2D(1, 1);
|
throw new InvalidOperationException("DrawMultiSelect couldn't find GraphicRayCaster in parents");
|
||||||
selectTexture.SetPixel(0, 0, new Color(1f, 1f, 1f, 0.6f));
|
|
||||||
selectTexture.Apply();
|
|
||||||
|
|
||||||
localRaycaster = GetComponentInParent<GraphicRaycaster>();
|
|
||||||
if (localRaycaster == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("DrawMultiSelect couldn't find GraphicRayCaster in parents");
|
|
||||||
}
|
|
||||||
|
|
||||||
preloaderRaycaster = Singleton<PreloaderUI>.Instance.transform.GetChild(0).GetComponent<GraphicRaycaster>();
|
|
||||||
if (preloaderRaycaster == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("DrawMultiSelect couldn't find the PreloaderUI GraphicRayCaster");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnDisable()
|
preloaderRaycaster = Singleton<PreloaderUI>.Instance.transform.GetChild(0).GetComponent<GraphicRaycaster>();
|
||||||
|
if (preloaderRaycaster == null)
|
||||||
{
|
{
|
||||||
drawing = false;
|
throw new InvalidOperationException("DrawMultiSelect couldn't find the PreloaderUI GraphicRayCaster");
|
||||||
MultiSelect.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (!MultiSelect.Enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// checking ItemUiContext is a quick and easy way to know the mouse is over an item
|
|
||||||
if (Input.GetKeyDown(Settings.SelectionBoxKey.Value.MainKey) && ItemUiContext.Instance.R().ItemContext == null)
|
|
||||||
{
|
|
||||||
PointerEventData eventData = new(EventSystem.current)
|
|
||||||
{
|
|
||||||
position = Input.mousePosition
|
|
||||||
};
|
|
||||||
|
|
||||||
List<RaycastResult> results = [];
|
|
||||||
localRaycaster.Raycast(eventData, results);
|
|
||||||
preloaderRaycaster.Raycast(eventData, results);
|
|
||||||
|
|
||||||
foreach (GameObject gameObject in results.Select(r => r.gameObject))
|
|
||||||
{
|
|
||||||
var draggables = gameObject.GetComponents<MonoBehaviour>()
|
|
||||||
.Where(c => c is IDragHandler || c is IBeginDragHandler || c is TextMeshProUGUI) // tmp_inputfield is draggable, but textmesh isn't so explicitly include
|
|
||||||
.Where(c => c is not ScrollRectNoDrag) // this disables scrolling, it doesn't add it
|
|
||||||
.Where(c => c.name != "Inner"); // there's a random DragTrigger sitting in ItemInfoWindows
|
|
||||||
|
|
||||||
var clickables = gameObject.GetComponents<MonoBehaviour>()
|
|
||||||
.Where(c => c is IPointerClickHandler || c is IPointerDownHandler || c is IPointerUpHandler);
|
|
||||||
|
|
||||||
if (draggables.Any() || clickables.Any())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectOrigin = Input.mousePosition;
|
|
||||||
drawing = true;
|
|
||||||
secondary = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drawing)
|
|
||||||
{
|
|
||||||
selectEnd = Input.mousePosition;
|
|
||||||
|
|
||||||
Rect selectRect = new(selectOrigin, selectEnd - selectOrigin);
|
|
||||||
|
|
||||||
// If not secondary, then we can kick out any non-rendered items, plus they won't be covered by the foreach below
|
|
||||||
if (!secondary)
|
|
||||||
{
|
|
||||||
MultiSelect.Prune();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (GridItemView gridItemView in transform.root.GetComponentsInChildren<GridItemView>().Concat(Singleton<PreloaderUI>.Instance.GetComponentsInChildren<GridItemView>()))
|
|
||||||
{
|
|
||||||
RectTransform itemTransform = gridItemView.GetComponent<RectTransform>();
|
|
||||||
Rect itemRect = new((Vector2)itemTransform.position + itemTransform.rect.position * itemTransform.lossyScale, itemTransform.rect.size * itemTransform.lossyScale);
|
|
||||||
|
|
||||||
if (selectRect.Overlaps(itemRect, true))
|
|
||||||
{
|
|
||||||
// Don't re-raycast already selected items - if there were visible before they still are
|
|
||||||
if (MultiSelect.IsSelected(gridItemView, secondary))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, ensure it's not overlapped by window UI
|
|
||||||
PointerEventData eventData = new(EventSystem.current);
|
|
||||||
|
|
||||||
if (IsOnTop(itemRect, itemTransform, preloaderRaycaster)) // no preloaderUI on top of this?
|
|
||||||
{
|
|
||||||
if (itemTransform.IsDescendantOf(Singleton<PreloaderUI>.Instance.transform))
|
|
||||||
{
|
|
||||||
MultiSelect.Select(gridItemView, secondary);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsOnTop(itemRect, itemTransform, localRaycaster)) // no local UI on top of this?
|
|
||||||
{
|
|
||||||
MultiSelect.Select(gridItemView, secondary);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiSelect.Deselect(gridItemView, secondary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drawing && !Input.GetKey(Settings.SelectionBoxKey.Value.MainKey))
|
|
||||||
{
|
|
||||||
drawing = false;
|
|
||||||
if (secondary)
|
|
||||||
{
|
|
||||||
MultiSelect.CombineSecondary();
|
|
||||||
secondary = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGUI()
|
|
||||||
{
|
|
||||||
if (drawing)
|
|
||||||
{
|
|
||||||
// Invert Y because GUI has upper-left origin
|
|
||||||
Rect area = new(selectOrigin.x, Screen.height - selectOrigin.y, selectEnd.x - selectOrigin.x, selectOrigin.y - selectEnd.y);
|
|
||||||
|
|
||||||
Rect lineArea = area;
|
|
||||||
lineArea.height = 1; // Top
|
|
||||||
GUI.DrawTexture(lineArea, selectTexture);
|
|
||||||
|
|
||||||
lineArea.y = area.yMax - 1; // Bottom
|
|
||||||
GUI.DrawTexture(lineArea, selectTexture);
|
|
||||||
|
|
||||||
lineArea = area;
|
|
||||||
lineArea.width = 1; // Left
|
|
||||||
GUI.DrawTexture(lineArea, selectTexture);
|
|
||||||
|
|
||||||
lineArea.x = area.xMax - 1; // Right
|
|
||||||
GUI.DrawTexture(lineArea, selectTexture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsOnTop(Rect itemRect, Transform itemTransform, GraphicRaycaster raycaster)
|
|
||||||
{
|
|
||||||
// Otherwise, ensure it's not overlapped by window UI
|
|
||||||
PointerEventData eventData = new(EventSystem.current);
|
|
||||||
|
|
||||||
float widthMargin = 0.1f * (itemRect.xMax - itemRect.xMin);
|
|
||||||
float heightMargin = 0.1f * (itemRect.yMax - itemRect.yMin);
|
|
||||||
|
|
||||||
List<RaycastResult> raycastResults = [];
|
|
||||||
|
|
||||||
// Lower left
|
|
||||||
eventData.position = new Vector2(itemRect.xMin + widthMargin, itemRect.yMin + heightMargin);
|
|
||||||
raycaster.Raycast(eventData, raycastResults);
|
|
||||||
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upper left
|
|
||||||
raycastResults.Clear();
|
|
||||||
eventData.position = new Vector2(itemRect.xMin + widthMargin, itemRect.yMax - heightMargin);
|
|
||||||
raycaster.Raycast(eventData, raycastResults);
|
|
||||||
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upper right
|
|
||||||
raycastResults.Clear();
|
|
||||||
eventData.position = new Vector2(itemRect.xMax - widthMargin, itemRect.yMax - heightMargin);
|
|
||||||
raycaster.Raycast(eventData, raycastResults);
|
|
||||||
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lower right
|
|
||||||
raycastResults.Clear();
|
|
||||||
eventData.position = new Vector2(itemRect.xMax - widthMargin, itemRect.yMin + heightMargin);
|
|
||||||
raycaster.Raycast(eventData, raycastResults);
|
|
||||||
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TransformExtensions
|
public void OnDisable()
|
||||||
{
|
{
|
||||||
public static bool IsDescendantOf(this Transform transform, Transform target)
|
drawing = false;
|
||||||
|
MultiSelect.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (!MultiSelect.Enabled)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checking ItemUiContext is a quick and easy way to know the mouse is over an item
|
||||||
|
if (Input.GetKeyDown(Settings.SelectionBoxKey.Value.MainKey) && ItemUiContext.Instance.R().ItemContext == null)
|
||||||
|
{
|
||||||
|
PointerEventData eventData = new(EventSystem.current)
|
||||||
|
{
|
||||||
|
position = Input.mousePosition
|
||||||
|
};
|
||||||
|
|
||||||
|
List<RaycastResult> results = [];
|
||||||
|
localRaycaster.Raycast(eventData, results);
|
||||||
|
preloaderRaycaster.Raycast(eventData, results);
|
||||||
|
|
||||||
|
foreach (GameObject gameObject in results.Select(r => r.gameObject))
|
||||||
|
{
|
||||||
|
var draggables = gameObject.GetComponents<MonoBehaviour>()
|
||||||
|
.Where(c => c is IDragHandler || c is IBeginDragHandler || c is TextMeshProUGUI) // tmp_inputfield is draggable, but textmesh isn't so explicitly include
|
||||||
|
.Where(c => c is not ScrollRectNoDrag) // this disables scrolling, it doesn't add it
|
||||||
|
.Where(c => c.name != "Inner"); // there's a random DragTrigger sitting in ItemInfoWindows
|
||||||
|
|
||||||
|
var clickables = gameObject.GetComponents<MonoBehaviour>()
|
||||||
|
.Where(c => c is IPointerClickHandler || c is IPointerDownHandler || c is IPointerUpHandler);
|
||||||
|
|
||||||
|
if (draggables.Any() || clickables.Any())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectOrigin = Input.mousePosition;
|
||||||
|
drawing = true;
|
||||||
|
secondary = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawing)
|
||||||
|
{
|
||||||
|
selectEnd = Input.mousePosition;
|
||||||
|
|
||||||
|
Rect selectRect = new(selectOrigin, selectEnd - selectOrigin);
|
||||||
|
|
||||||
|
// If not secondary, then we can kick out any non-rendered items, plus they won't be covered by the foreach below
|
||||||
|
if (!secondary)
|
||||||
|
{
|
||||||
|
MultiSelect.Prune();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (GridItemView gridItemView in transform.root.GetComponentsInChildren<GridItemView>().Concat(Singleton<PreloaderUI>.Instance.GetComponentsInChildren<GridItemView>()))
|
||||||
|
{
|
||||||
|
RectTransform itemTransform = gridItemView.GetComponent<RectTransform>();
|
||||||
|
Rect itemRect = new((Vector2)itemTransform.position + itemTransform.rect.position * itemTransform.lossyScale, itemTransform.rect.size * itemTransform.lossyScale);
|
||||||
|
|
||||||
|
if (selectRect.Overlaps(itemRect, true))
|
||||||
|
{
|
||||||
|
// Don't re-raycast already selected items - if there were visible before they still are
|
||||||
|
if (MultiSelect.IsSelected(gridItemView, secondary))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, ensure it's not overlapped by window UI
|
||||||
|
PointerEventData eventData = new(EventSystem.current);
|
||||||
|
|
||||||
|
if (IsOnTop(itemRect, itemTransform, preloaderRaycaster)) // no preloaderUI on top of this?
|
||||||
|
{
|
||||||
|
if (itemTransform.IsDescendantOf(Singleton<PreloaderUI>.Instance.transform))
|
||||||
|
{
|
||||||
|
MultiSelect.Select(gridItemView, secondary);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsOnTop(itemRect, itemTransform, localRaycaster)) // no local UI on top of this?
|
||||||
|
{
|
||||||
|
MultiSelect.Select(gridItemView, secondary);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiSelect.Deselect(gridItemView, secondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drawing && !Input.GetKey(Settings.SelectionBoxKey.Value.MainKey))
|
||||||
|
{
|
||||||
|
drawing = false;
|
||||||
|
if (secondary)
|
||||||
|
{
|
||||||
|
MultiSelect.CombineSecondary();
|
||||||
|
secondary = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
if (drawing)
|
||||||
|
{
|
||||||
|
// Invert Y because GUI has upper-left origin
|
||||||
|
Rect area = new(selectOrigin.x, Screen.height - selectOrigin.y, selectEnd.x - selectOrigin.x, selectOrigin.y - selectEnd.y);
|
||||||
|
|
||||||
|
Rect lineArea = area;
|
||||||
|
lineArea.height = 1; // Top
|
||||||
|
GUI.DrawTexture(lineArea, selectTexture);
|
||||||
|
|
||||||
|
lineArea.y = area.yMax - 1; // Bottom
|
||||||
|
GUI.DrawTexture(lineArea, selectTexture);
|
||||||
|
|
||||||
|
lineArea = area;
|
||||||
|
lineArea.width = 1; // Left
|
||||||
|
GUI.DrawTexture(lineArea, selectTexture);
|
||||||
|
|
||||||
|
lineArea.x = area.xMax - 1; // Right
|
||||||
|
GUI.DrawTexture(lineArea, selectTexture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsOnTop(Rect itemRect, Transform itemTransform, GraphicRaycaster raycaster)
|
||||||
|
{
|
||||||
|
// Otherwise, ensure it's not overlapped by window UI
|
||||||
|
PointerEventData eventData = new(EventSystem.current);
|
||||||
|
|
||||||
|
float widthMargin = 0.1f * (itemRect.xMax - itemRect.xMin);
|
||||||
|
float heightMargin = 0.1f * (itemRect.yMax - itemRect.yMin);
|
||||||
|
|
||||||
|
List<RaycastResult> raycastResults = [];
|
||||||
|
|
||||||
|
// Lower left
|
||||||
|
eventData.position = new Vector2(itemRect.xMin + widthMargin, itemRect.yMin + heightMargin);
|
||||||
|
raycaster.Raycast(eventData, raycastResults);
|
||||||
|
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upper left
|
||||||
|
raycastResults.Clear();
|
||||||
|
eventData.position = new Vector2(itemRect.xMin + widthMargin, itemRect.yMax - heightMargin);
|
||||||
|
raycaster.Raycast(eventData, raycastResults);
|
||||||
|
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upper right
|
||||||
|
raycastResults.Clear();
|
||||||
|
eventData.position = new Vector2(itemRect.xMax - widthMargin, itemRect.yMax - heightMargin);
|
||||||
|
raycaster.Raycast(eventData, raycastResults);
|
||||||
|
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lower right
|
||||||
|
raycastResults.Clear();
|
||||||
|
eventData.position = new Vector2(itemRect.xMax - widthMargin, itemRect.yMin + heightMargin);
|
||||||
|
raycaster.Raycast(eventData, raycastResults);
|
||||||
|
if (raycastResults.Any() && !raycastResults[0].gameObject.transform.IsDescendantOf(itemTransform))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TransformExtensions
|
||||||
|
{
|
||||||
|
public static bool IsDescendantOf(this Transform transform, Transform target)
|
||||||
|
{
|
||||||
|
if (transform == target)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (transform.parent != null)
|
||||||
|
{
|
||||||
|
transform = transform.parent;
|
||||||
if (transform == target)
|
if (transform == target)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (transform.parent != null)
|
|
||||||
{
|
|
||||||
transform = transform.parent;
|
|
||||||
if (transform == target)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,123 +5,122 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class MultiGrid
|
||||||
{
|
{
|
||||||
public static class MultiGrid
|
private static readonly Dictionary<string, Dictionary<string, Vector2Int>> GridOffsets = [];
|
||||||
|
private static readonly Dictionary<string, Dictionary<int, Dictionary<int, string>>> GridsByLocation = [];
|
||||||
|
|
||||||
|
public static LocationInGrid GetGridLocation(GridItemAddress realAddress)
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, Dictionary<string, Vector2Int>> GridOffsets = [];
|
if (!IsMultiGrid(realAddress))
|
||||||
private static readonly Dictionary<string, Dictionary<int, Dictionary<int, string>>> GridsByLocation = [];
|
|
||||||
|
|
||||||
public static LocationInGrid GetGridLocation(GridItemAddress realAddress)
|
|
||||||
{
|
{
|
||||||
if (!IsMultiGrid(realAddress))
|
return realAddress.LocationInGrid;
|
||||||
{
|
|
||||||
return realAddress.LocationInGrid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2Int gridOffset = GridOffsets[realAddress.Container.ParentItem.TemplateId][realAddress.Grid.Id];
|
|
||||||
return new LocationInGrid(realAddress.LocationInGrid.x + gridOffset.x, realAddress.LocationInGrid.y + gridOffset.y, realAddress.LocationInGrid.r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GridItemAddress GetRealAddress(StashGridClass originGrid, LocationInGrid multigridLocation)
|
Vector2Int gridOffset = GridOffsets[realAddress.Container.ParentItem.TemplateId][realAddress.Grid.Id];
|
||||||
|
return new LocationInGrid(realAddress.LocationInGrid.x + gridOffset.x, realAddress.LocationInGrid.y + gridOffset.y, realAddress.LocationInGrid.r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GridItemAddress GetRealAddress(StashGridClass originGrid, LocationInGrid multigridLocation)
|
||||||
|
{
|
||||||
|
if (!IsMultiGrid(originGrid.ParentItem))
|
||||||
{
|
{
|
||||||
if (!IsMultiGrid(originGrid.ParentItem))
|
// Clamp to the actual grid
|
||||||
{
|
multigridLocation.x = Math.Max(0, Math.Min(originGrid.GridWidth.Value, multigridLocation.x));
|
||||||
// Clamp to the actual grid
|
multigridLocation.y = Math.Max(0, Math.Min(originGrid.GridHeight.Value, multigridLocation.y));
|
||||||
multigridLocation.x = Math.Max(0, Math.Min(originGrid.GridWidth.Value, multigridLocation.x));
|
|
||||||
multigridLocation.y = Math.Max(0, Math.Min(originGrid.GridHeight.Value, multigridLocation.y));
|
|
||||||
|
|
||||||
return new GridItemAddress(originGrid, multigridLocation);
|
return new GridItemAddress(originGrid, multigridLocation);
|
||||||
}
|
|
||||||
|
|
||||||
var gridsByLocation = GridsByLocation[originGrid.ParentItem.TemplateId];
|
|
||||||
|
|
||||||
// Clamp to known "meta" grid
|
|
||||||
int x = Math.Max(0, Math.Min(gridsByLocation.Keys.Max(), multigridLocation.x));
|
|
||||||
int y = Math.Max(0, Math.Min(gridsByLocation[x].Keys.Max(), multigridLocation.y));
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
if (!gridsByLocation.ContainsKey(x) || !gridsByLocation[x].ContainsKey(y))
|
|
||||||
{
|
|
||||||
// Perhaps some weird layout with gaps in the middle? Fall back to a known good
|
|
||||||
x = gridsByLocation.Keys.First();
|
|
||||||
y = gridsByLocation[x].Keys.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
string gridId = gridsByLocation[x][y];
|
|
||||||
StashGridClass grid = (originGrid.ParentItem as LootItemClass).Grids.Single(g => g.Id == gridId);
|
|
||||||
Vector2Int offsets = GridOffsets[originGrid.ParentItem.TemplateId][gridId];
|
|
||||||
|
|
||||||
LocationInGrid location = new(x - offsets.x, y - offsets.y, multigridLocation.r);
|
|
||||||
return new GridItemAddress(grid, location);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Cache(GridView initialGridView)
|
var gridsByLocation = GridsByLocation[originGrid.ParentItem.TemplateId];
|
||||||
|
|
||||||
|
// Clamp to known "meta" grid
|
||||||
|
int x = Math.Max(0, Math.Min(gridsByLocation.Keys.Max(), multigridLocation.x));
|
||||||
|
int y = Math.Max(0, Math.Min(gridsByLocation[x].Keys.Max(), multigridLocation.y));
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if (!gridsByLocation.ContainsKey(x) || !gridsByLocation[x].ContainsKey(y))
|
||||||
{
|
{
|
||||||
if (initialGridView == null)
|
// Perhaps some weird layout with gaps in the middle? Fall back to a known good
|
||||||
|
x = gridsByLocation.Keys.First();
|
||||||
|
y = gridsByLocation[x].Keys.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
string gridId = gridsByLocation[x][y];
|
||||||
|
StashGridClass grid = (originGrid.ParentItem as LootItemClass).Grids.Single(g => g.Id == gridId);
|
||||||
|
Vector2Int offsets = GridOffsets[originGrid.ParentItem.TemplateId][gridId];
|
||||||
|
|
||||||
|
LocationInGrid location = new(x - offsets.x, y - offsets.y, multigridLocation.r);
|
||||||
|
return new GridItemAddress(grid, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Cache(GridView initialGridView)
|
||||||
|
{
|
||||||
|
if (initialGridView == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Item parent = initialGridView.Grid.ParentItem;
|
||||||
|
if (GridOffsets.ContainsKey(parent.TemplateId) || !IsMultiGrid(parent))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, Vector2Int> gridOffsets = [];
|
||||||
|
Dictionary<int, Dictionary<int, string>> gridsByLocation = [];
|
||||||
|
|
||||||
|
// Sometimes the parent's pivot is 0,1; sometimes it's 0,0. Thanks BSG
|
||||||
|
RectTransform parentView = initialGridView.transform.parent.RectTransform();
|
||||||
|
Vector2 parentPosition = parentView.pivot.y == 1 ? parentView.position : new Vector2(parentView.position.x, parentView.position.y + parentView.sizeDelta.y);
|
||||||
|
|
||||||
|
Vector2 gridSize = new(64f * parentView.lossyScale.x, 64f * parentView.lossyScale.y);
|
||||||
|
|
||||||
|
foreach (GridView gridView in parentView.GetComponentsInChildren<GridView>())
|
||||||
|
{
|
||||||
|
// Get absolute offsets
|
||||||
|
float xOffset = gridView.transform.position.x - parentPosition.x;
|
||||||
|
float yOffset = -(gridView.transform.position.y - parentPosition.y); // invert y since grid coords are upper-left origin
|
||||||
|
|
||||||
|
int x = (int)Math.Round(xOffset / gridSize.x, MidpointRounding.AwayFromZero);
|
||||||
|
int y = (int)Math.Round(yOffset / gridSize.y, MidpointRounding.AwayFromZero);
|
||||||
|
|
||||||
|
gridOffsets.Add(gridView.Grid.Id, new Vector2Int(x, y));
|
||||||
|
|
||||||
|
// Populate reverse lookup
|
||||||
|
for (int i = 0; i < gridView.Grid.GridWidth.Value; i++)
|
||||||
{
|
{
|
||||||
return;
|
if (!gridsByLocation.ContainsKey(x + i))
|
||||||
}
|
|
||||||
|
|
||||||
Item parent = initialGridView.Grid.ParentItem;
|
|
||||||
if (GridOffsets.ContainsKey(parent.TemplateId) || !IsMultiGrid(parent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<string, Vector2Int> gridOffsets = [];
|
|
||||||
Dictionary<int, Dictionary<int, string>> gridsByLocation = [];
|
|
||||||
|
|
||||||
// Sometimes the parent's pivot is 0,1; sometimes it's 0,0. Thanks BSG
|
|
||||||
RectTransform parentView = initialGridView.transform.parent.RectTransform();
|
|
||||||
Vector2 parentPosition = parentView.pivot.y == 1 ? parentView.position : new Vector2(parentView.position.x, parentView.position.y + parentView.sizeDelta.y);
|
|
||||||
|
|
||||||
Vector2 gridSize = new(64f * parentView.lossyScale.x, 64f * parentView.lossyScale.y);
|
|
||||||
|
|
||||||
foreach (GridView gridView in parentView.GetComponentsInChildren<GridView>())
|
|
||||||
{
|
|
||||||
// Get absolute offsets
|
|
||||||
float xOffset = gridView.transform.position.x - parentPosition.x;
|
|
||||||
float yOffset = -(gridView.transform.position.y - parentPosition.y); // invert y since grid coords are upper-left origin
|
|
||||||
|
|
||||||
int x = (int)Math.Round(xOffset / gridSize.x, MidpointRounding.AwayFromZero);
|
|
||||||
int y = (int)Math.Round(yOffset / gridSize.y, MidpointRounding.AwayFromZero);
|
|
||||||
|
|
||||||
gridOffsets.Add(gridView.Grid.Id, new Vector2Int(x, y));
|
|
||||||
|
|
||||||
// Populate reverse lookup
|
|
||||||
for (int i = 0; i < gridView.Grid.GridWidth.Value; i++)
|
|
||||||
{
|
{
|
||||||
if (!gridsByLocation.ContainsKey(x + i))
|
gridsByLocation.Add(x + i, []);
|
||||||
{
|
}
|
||||||
gridsByLocation.Add(x + i, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
var rowGrids = gridsByLocation[x + i];
|
var rowGrids = gridsByLocation[x + i];
|
||||||
for (int j = 0; j < gridView.Grid.GridHeight.Value; j++)
|
for (int j = 0; j < gridView.Grid.GridHeight.Value; j++)
|
||||||
{
|
{
|
||||||
rowGrids.Add(y + j, gridView.Grid.Id);
|
rowGrids.Add(y + j, gridView.Grid.Id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridOffsets.Add(parent.TemplateId, gridOffsets);
|
|
||||||
GridsByLocation.Add(parent.TemplateId, gridsByLocation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsMultiGrid(GridItemAddress itemAddress)
|
GridOffsets.Add(parent.TemplateId, gridOffsets);
|
||||||
|
GridsByLocation.Add(parent.TemplateId, gridsByLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsMultiGrid(GridItemAddress itemAddress)
|
||||||
|
{
|
||||||
|
return IsMultiGrid(itemAddress.Container.ParentItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsMultiGrid(Item item)
|
||||||
|
{
|
||||||
|
if (item is not LootItemClass lootItem)
|
||||||
{
|
{
|
||||||
return IsMultiGrid(itemAddress.Container.ParentItem);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsMultiGrid(Item item)
|
return lootItem.Grids.Length > 1;
|
||||||
{
|
|
||||||
if (item is not LootItemClass lootItem)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lootItem.Grids.Length > 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,62 +2,61 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class MultiSelectDebug : MonoBehaviour
|
||||||
{
|
{
|
||||||
public class MultiSelectDebug : MonoBehaviour
|
private GUIStyle guiStyle;
|
||||||
|
private Rect guiRect = new(20, 70, 0, 0);
|
||||||
|
|
||||||
|
GUIContent guiContent;
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
{
|
{
|
||||||
private GUIStyle guiStyle;
|
if (!MultiSelect.Enabled || !Settings.ShowMultiSelectDebug.Value)
|
||||||
private Rect guiRect = new(20, 70, 0, 0);
|
|
||||||
|
|
||||||
GUIContent guiContent;
|
|
||||||
|
|
||||||
public void OnGUI()
|
|
||||||
{
|
{
|
||||||
if (!MultiSelect.Enabled || !Settings.ShowMultiSelectDebug.Value)
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
guiStyle ??= new GUIStyle(GUI.skin.box)
|
|
||||||
{
|
|
||||||
alignment = TextAnchor.MiddleLeft,
|
|
||||||
fontSize = 14,
|
|
||||||
margin = new RectOffset(3, 3, 3, 3),
|
|
||||||
richText = true
|
|
||||||
};
|
|
||||||
|
|
||||||
guiContent ??= new GUIContent();
|
|
||||||
|
|
||||||
StringBuilder builder = new();
|
|
||||||
|
|
||||||
builder.Append("<b>MultiSelect</b>\n");
|
|
||||||
builder.AppendFormat("Active: <color={0}>{1}</color>\n", MultiSelect.Active ? "green" : "red", MultiSelect.Active);
|
|
||||||
builder.AppendFormat("Items: <color=yellow>{0}</color>\n", MultiSelect.Count);
|
|
||||||
|
|
||||||
foreach (DragItemContext itemContext in MultiSelect.SortedItemContexts())
|
|
||||||
{
|
|
||||||
LocationInGrid location = itemContext.ItemAddress is GridItemAddress gridAddress ? MultiGrid.GetGridLocation(gridAddress) : null;
|
|
||||||
builder.AppendFormat("x{0} {1} {2} {3}\n",
|
|
||||||
itemContext.Item.StackObjectsCount,
|
|
||||||
itemContext.ItemAddress.Container.ID,
|
|
||||||
location != null ? $"({location.x}, {location.y})" : "(slot)",
|
|
||||||
itemContext.Item.Name.Localized());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MultiSelect.SecondaryContexts.Any())
|
|
||||||
{
|
|
||||||
builder.AppendFormat("Secondary Items: <color=yellow>{0}</color>\n", MultiSelect.SecondaryCount);
|
|
||||||
foreach (DragItemContext itemContext in MultiSelect.SecondaryContexts)
|
|
||||||
{
|
|
||||||
builder.AppendFormat("x{0} {1}\n", itemContext.Item.StackObjectsCount, itemContext.Item.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guiContent.text = builder.ToString();
|
|
||||||
|
|
||||||
guiRect.size = guiStyle.CalcSize(guiContent);
|
|
||||||
|
|
||||||
GUI.Box(guiRect, guiContent, guiStyle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guiStyle ??= new GUIStyle(GUI.skin.box)
|
||||||
|
{
|
||||||
|
alignment = TextAnchor.MiddleLeft,
|
||||||
|
fontSize = 14,
|
||||||
|
margin = new RectOffset(3, 3, 3, 3),
|
||||||
|
richText = true
|
||||||
|
};
|
||||||
|
|
||||||
|
guiContent ??= new GUIContent();
|
||||||
|
|
||||||
|
StringBuilder builder = new();
|
||||||
|
|
||||||
|
builder.Append("<b>MultiSelect</b>\n");
|
||||||
|
builder.AppendFormat("Active: <color={0}>{1}</color>\n", MultiSelect.Active ? "green" : "red", MultiSelect.Active);
|
||||||
|
builder.AppendFormat("Items: <color=yellow>{0}</color>\n", MultiSelect.Count);
|
||||||
|
|
||||||
|
foreach (DragItemContext itemContext in MultiSelect.SortedItemContexts())
|
||||||
|
{
|
||||||
|
LocationInGrid location = itemContext.ItemAddress is GridItemAddress gridAddress ? MultiGrid.GetGridLocation(gridAddress) : null;
|
||||||
|
builder.AppendFormat("x{0} {1} {2} {3}\n",
|
||||||
|
itemContext.Item.StackObjectsCount,
|
||||||
|
itemContext.ItemAddress.Container.ID,
|
||||||
|
location != null ? $"({location.x}, {location.y})" : "(slot)",
|
||||||
|
itemContext.Item.Name.Localized());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MultiSelect.SecondaryContexts.Any())
|
||||||
|
{
|
||||||
|
builder.AppendFormat("Secondary Items: <color=yellow>{0}</color>\n", MultiSelect.SecondaryCount);
|
||||||
|
foreach (DragItemContext itemContext in MultiSelect.SecondaryContexts)
|
||||||
|
{
|
||||||
|
builder.AppendFormat("x{0} {1}\n", itemContext.Item.StackObjectsCount, itemContext.Item.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guiContent.text = builder.ToString();
|
||||||
|
|
||||||
|
guiRect.size = guiStyle.CalcSize(guiContent);
|
||||||
|
|
||||||
|
GUI.Box(guiRect, guiContent, guiStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,90 +9,89 @@ using UnityEngine;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class AddOfferClickablePricesPatches
|
||||||
{
|
{
|
||||||
public static class AddOfferClickablePricesPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new AddButtonPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddButtonPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new AddButtonPatch().Enable();
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Show));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AddButtonPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(AddOfferWindow __instance, ItemMarketPricesPanel ____pricesPanel, RequirementView[] ____requirementViews)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var panel = ____pricesPanel.R();
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
var rublesRequirement = ____requirementViews.First(rv => rv.name == "Requirement (RUB)");
|
||||||
public static void Postfix(AddOfferWindow __instance, ItemMarketPricesPanel ____pricesPanel, RequirementView[] ____requirementViews)
|
|
||||||
{
|
|
||||||
var panel = ____pricesPanel.R();
|
|
||||||
|
|
||||||
var rublesRequirement = ____requirementViews.First(rv => rv.name == "Requirement (RUB)");
|
Button lowestButton = panel.LowestLabel.GetOrAddComponent<HighlightButton>();
|
||||||
|
lowestButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Minimum));
|
||||||
|
____pricesPanel.AddDisposable(lowestButton.onClick.RemoveAllListeners);
|
||||||
|
|
||||||
Button lowestButton = panel.LowestLabel.GetOrAddComponent<HighlightButton>();
|
Button averageButton = panel.AverageLabel.GetOrAddComponent<HighlightButton>();
|
||||||
lowestButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Minimum));
|
averageButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Average));
|
||||||
____pricesPanel.AddDisposable(lowestButton.onClick.RemoveAllListeners);
|
____pricesPanel.AddDisposable(averageButton.onClick.RemoveAllListeners);
|
||||||
|
|
||||||
Button averageButton = panel.AverageLabel.GetOrAddComponent<HighlightButton>();
|
Button maximumButton = panel.MaximumLabel.GetOrAddComponent<HighlightButton>();
|
||||||
averageButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Average));
|
maximumButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Maximum));
|
||||||
____pricesPanel.AddDisposable(averageButton.onClick.RemoveAllListeners);
|
____pricesPanel.AddDisposable(maximumButton.onClick.RemoveAllListeners);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Button maximumButton = panel.MaximumLabel.GetOrAddComponent<HighlightButton>();
|
private static void SetRequirement(AddOfferWindow window, RequirementView requirement, float price)
|
||||||
maximumButton.onClick.AddListener(() => SetRequirement(__instance, rublesRequirement, ____pricesPanel.Maximum));
|
{
|
||||||
____pricesPanel.AddDisposable(maximumButton.onClick.RemoveAllListeners);
|
if (window.R().BulkOffer)
|
||||||
}
|
{
|
||||||
|
price *= window.Int32_0; // offer item count
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetRequirement(AddOfferWindow window, RequirementView requirement, float price)
|
requirement.method_0(price.ToString("F0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HighlightButton : Button
|
||||||
|
{
|
||||||
|
private Color originalColor;
|
||||||
|
bool originalOverrideColorTags;
|
||||||
|
|
||||||
|
private TextMeshProUGUI _text;
|
||||||
|
private TextMeshProUGUI Text
|
||||||
{
|
{
|
||||||
if (window.R().BulkOffer)
|
get
|
||||||
{
|
{
|
||||||
price *= window.Int32_0; // offer item count
|
if (_text == null)
|
||||||
}
|
|
||||||
|
|
||||||
requirement.method_0(price.ToString("F0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HighlightButton : Button
|
|
||||||
{
|
|
||||||
private Color originalColor;
|
|
||||||
bool originalOverrideColorTags;
|
|
||||||
|
|
||||||
private TextMeshProUGUI _text;
|
|
||||||
private TextMeshProUGUI Text
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
{
|
||||||
if (_text == null)
|
_text = GetComponent<TextMeshProUGUI>();
|
||||||
{
|
|
||||||
_text = GetComponent<TextMeshProUGUI>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return _text;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnPointerEnter([NotNull] PointerEventData eventData)
|
public override void OnPointerEnter([NotNull] PointerEventData eventData)
|
||||||
{
|
{
|
||||||
base.OnPointerEnter(eventData);
|
base.OnPointerEnter(eventData);
|
||||||
|
|
||||||
originalColor = Text.color;
|
originalColor = Text.color;
|
||||||
originalOverrideColorTags = Text.overrideColorTags;
|
originalOverrideColorTags = Text.overrideColorTags;
|
||||||
|
|
||||||
Text.overrideColorTags = true;
|
Text.overrideColorTags = true;
|
||||||
Text.color = Color.white;
|
Text.color = Color.white;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnPointerExit([NotNull] PointerEventData eventData)
|
public override void OnPointerExit([NotNull] PointerEventData eventData)
|
||||||
{
|
{
|
||||||
base.OnPointerExit(eventData);
|
base.OnPointerExit(eventData);
|
||||||
|
|
||||||
Text.overrideColorTags = originalOverrideColorTags;
|
Text.overrideColorTags = originalOverrideColorTags;
|
||||||
Text.color = originalColor;
|
Text.color = originalColor;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,57 +5,56 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class AddOfferRememberAutoselectPatches
|
||||||
{
|
{
|
||||||
public static class AddOfferRememberAutoselectPatches
|
private static readonly string PlayerPrefKey = "UIFixes.AddOffer.AutoselectSimilar";
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static readonly string PlayerPrefKey = "UIFixes.AddOffer.AutoselectSimilar";
|
new RememberAutoselectPatch().Enable();
|
||||||
|
new RestoreAutoselectPatch().Enable();
|
||||||
|
|
||||||
public static void Enable()
|
Settings.RememberAutoselectSimilar.Subscribe(enabled =>
|
||||||
{
|
{
|
||||||
new RememberAutoselectPatch().Enable();
|
if (!enabled)
|
||||||
new RestoreAutoselectPatch().Enable();
|
|
||||||
|
|
||||||
Settings.RememberAutoselectSimilar.Subscribe(enabled =>
|
|
||||||
{
|
{
|
||||||
if (!enabled)
|
PlayerPrefs.DeleteKey(PlayerPrefKey);
|
||||||
{
|
}
|
||||||
PlayerPrefs.DeleteKey(PlayerPrefKey);
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
public class RememberAutoselectPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_7));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RememberAutoselectPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(bool value)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (Settings.RememberAutoselectSimilar.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_7));
|
PlayerPrefs.SetInt(PlayerPrefKey, value ? 1 : 0);
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(bool value)
|
|
||||||
{
|
|
||||||
if (Settings.RememberAutoselectSimilar.Value)
|
|
||||||
{
|
|
||||||
PlayerPrefs.SetInt(PlayerPrefKey, value ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class RestoreAutoselectPatch : ModulePatch
|
public class RestoreAutoselectPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Awake));
|
||||||
{
|
}
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Awake));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
[PatchPrefix]
|
||||||
public static void Prefix(UpdatableToggle ____autoSelectSimilar)
|
public static void Prefix(UpdatableToggle ____autoSelectSimilar)
|
||||||
|
{
|
||||||
|
if (Settings.RememberAutoselectSimilar.Value && PlayerPrefs.HasKey(PlayerPrefKey))
|
||||||
{
|
{
|
||||||
if (Settings.RememberAutoselectSimilar.Value && PlayerPrefs.HasKey(PlayerPrefKey))
|
____autoSelectSimilar.UpdateValue(PlayerPrefs.GetInt(PlayerPrefKey) == 1);
|
||||||
{
|
|
||||||
____autoSelectSimilar.UpdateValue(PlayerPrefs.GetInt(PlayerPrefKey) == 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,68 +6,67 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
{
|
|
||||||
public static class AimToggleHoldPatches
|
|
||||||
{
|
|
||||||
public static void Enable()
|
|
||||||
{
|
|
||||||
new AddStatesPatch().Enable();
|
|
||||||
new UpdateInputPatch().Enable();
|
|
||||||
|
|
||||||
Settings.ToggleOrHoldAim.SettingChanged += (_, _) =>
|
public static class AimToggleHoldPatches
|
||||||
{
|
{
|
||||||
// Will "save" control settings, running GClass1911.UpdateInput, which will set (or unset) toggle/hold behavior
|
public static void Enable()
|
||||||
Singleton<SharedGameSettingsClass>.Instance.Control.Controller.method_3();
|
{
|
||||||
};
|
new AddStatesPatch().Enable();
|
||||||
|
new UpdateInputPatch().Enable();
|
||||||
|
|
||||||
|
Settings.ToggleOrHoldAim.SettingChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
// Will "save" control settings, running GClass1911.UpdateInput, which will set (or unset) toggle/hold behavior
|
||||||
|
Singleton<SharedGameSettingsClass>.Instance.Control.Controller.method_3();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddStatesPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static FieldInfo StateMachineArray;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
StateMachineArray = AccessTools.Field(typeof(KeyCombination), "keyCombinationState_1");
|
||||||
|
return AccessTools.GetDeclaredConstructors(typeof(ToggleKeyCombination)).Single();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AddStatesPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ToggleKeyCombination __instance, EGameKey gameKey, ECommand disableCommand, KeyCombination.KeyCombinationState[] ___keyCombinationState_1)
|
||||||
{
|
{
|
||||||
private static FieldInfo StateMachineArray;
|
if (!Settings.ToggleOrHoldAim.Value || gameKey != EGameKey.Aim)
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
StateMachineArray = AccessTools.Field(typeof(KeyCombination), "keyCombinationState_1");
|
return;
|
||||||
return AccessTools.GetDeclaredConstructors(typeof(ToggleKeyCombination)).Single();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
List<KeyCombination.KeyCombinationState> states = new(___keyCombinationState_1)
|
||||||
public static void Postfix(ToggleKeyCombination __instance, EGameKey gameKey, ECommand disableCommand, KeyCombination.KeyCombinationState[] ___keyCombinationState_1)
|
|
||||||
{
|
|
||||||
if (!Settings.ToggleOrHoldAim.Value || gameKey != EGameKey.Aim)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<KeyCombination.KeyCombinationState> states = new(___keyCombinationState_1)
|
|
||||||
{
|
{
|
||||||
new ToggleHoldIdleState(__instance),
|
new ToggleHoldIdleState(__instance),
|
||||||
new ToggleHoldClickOrHoldState(__instance),
|
new ToggleHoldClickOrHoldState(__instance),
|
||||||
new ToggleHoldHoldState(__instance, disableCommand)
|
new ToggleHoldHoldState(__instance, disableCommand)
|
||||||
};
|
};
|
||||||
|
|
||||||
StateMachineArray.SetValue(__instance, states.ToArray());
|
StateMachineArray.SetValue(__instance, states.ToArray());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpdateInputPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(KeyCombination), nameof(KeyCombination.UpdateInput));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UpdateInputPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(KeyCombination __instance)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.ToggleOrHoldAim.Value || __instance.GameKey != EGameKey.Aim)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(KeyCombination), nameof(KeyCombination.UpdateInput));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
__instance.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle);
|
||||||
public static void Postfix(KeyCombination __instance)
|
|
||||||
{
|
|
||||||
if (!Settings.ToggleOrHoldAim.Value || __instance.GameKey != EGameKey.Aim)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
__instance.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,56 +9,55 @@ using System.Collections.Generic;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
{
|
|
||||||
public class AssortUnlocksPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static bool Loading = false;
|
|
||||||
private static Dictionary<string, string> AssortUnlocks = null;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
public class AssortUnlocksPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static bool Loading = false;
|
||||||
|
private static Dictionary<string, string> AssortUnlocks = null;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(OfferView), nameof(OfferView.method_10));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(OfferView __instance, HoverTooltipArea ____hoverTooltipArea)
|
||||||
|
{
|
||||||
|
if (!Settings.ShowRequiredQuest.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(OfferView), nameof(OfferView.method_10));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (AssortUnlocks == null && !Loading)
|
||||||
public static void Postfix(OfferView __instance, HoverTooltipArea ____hoverTooltipArea)
|
|
||||||
{
|
{
|
||||||
if (!Settings.ShowRequiredQuest.Value)
|
Loading = true;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AssortUnlocks == null && !Loading)
|
Task<string> response = RequestHandler.GetJsonAsync("/uifixes/assortUnlocks");
|
||||||
|
response.ContinueWith(task =>
|
||||||
{
|
{
|
||||||
Loading = true;
|
string json = task.Result;
|
||||||
|
if (!String.IsNullOrEmpty(json))
|
||||||
Task<string> response = RequestHandler.GetJsonAsync("/uifixes/assortUnlocks");
|
|
||||||
response.ContinueWith(task =>
|
|
||||||
{
|
{
|
||||||
string json = task.Result;
|
try
|
||||||
if (!String.IsNullOrEmpty(json))
|
|
||||||
{
|
{
|
||||||
try
|
AssortUnlocks = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
|
||||||
{
|
}
|
||||||
AssortUnlocks = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
|
catch (Exception ex)
|
||||||
}
|
{
|
||||||
catch (Exception ex)
|
Logger.LogError(ex);
|
||||||
{
|
|
||||||
Logger.LogError(ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__instance.Offer_0.Locked)
|
|
||||||
{
|
|
||||||
if (AssortUnlocks != null && AssortUnlocks.TryGetValue(__instance.Offer_0.Item.Id, out string questName))
|
|
||||||
{
|
|
||||||
____hoverTooltipArea.SetMessageText(____hoverTooltipArea.String_1 + " (" + questName.Localized() + ")", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__instance.Offer_0.Locked)
|
||||||
|
{
|
||||||
|
if (AssortUnlocks != null && AssortUnlocks.TryGetValue(__instance.Offer_0.Item.Id, out string questName))
|
||||||
|
{
|
||||||
|
____hoverTooltipArea.SetMessageText(____hoverTooltipArea.String_1 + " (" + questName.Localized() + ")", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,30 +4,29 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class AutofillQuestItemsPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class AutofillQuestItemsPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.DeclaredMethod(typeof(HandoverQuestItemsWindow), nameof(HandoverQuestItemsWindow.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(HandoverQuestItemsWindow __instance)
|
||||||
|
{
|
||||||
|
if (Settings.AutofillQuestTurnIns.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.DeclaredMethod(typeof(HandoverQuestItemsWindow), nameof(HandoverQuestItemsWindow.Show));
|
__instance.AutoSelectButtonPressedHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
// Apparently they never set up the scroll correctly?
|
||||||
public static void Postfix(HandoverQuestItemsWindow __instance)
|
Transform scrollArea = __instance.transform.Find("Window/Content/Possessions Grid/Scroll Area");
|
||||||
|
if (scrollArea != null)
|
||||||
{
|
{
|
||||||
if (Settings.AutofillQuestTurnIns.Value)
|
ScrollRectNoDrag scroller = scrollArea.GetComponent<ScrollRectNoDrag>();
|
||||||
{
|
scroller.content = scrollArea.Find("GridView")?.RectTransform();
|
||||||
__instance.AutoSelectButtonPressedHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apparently they never set up the scroll correctly?
|
|
||||||
Transform scrollArea = __instance.transform.Find("Window/Content/Possessions Grid/Scroll Area");
|
|
||||||
if (scrollArea != null)
|
|
||||||
{
|
|
||||||
ScrollRectNoDrag scroller = scrollArea.GetComponent<ScrollRectNoDrag>();
|
|
||||||
scroller.content = scrollArea.Find("GridView")?.RectTransform();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,220 +6,219 @@ using System.Reflection;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class ConfirmDialogKeysPatches
|
||||||
{
|
{
|
||||||
public static class ConfirmDialogKeysPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new DialogWindowPatch().Enable();
|
||||||
{
|
new ItemUiContextWindowPatch().Enable();
|
||||||
new DialogWindowPatch().Enable();
|
new ErrorScreenPatch().Enable();
|
||||||
new ItemUiContextWindowPatch().Enable();
|
new AddOfferPatch().Enable();
|
||||||
new ErrorScreenPatch().Enable();
|
|
||||||
new AddOfferPatch().Enable();
|
|
||||||
|
|
||||||
new ClickOutPatch().Enable();
|
new ClickOutPatch().Enable();
|
||||||
new ClickOutSplitDialogPatch().Enable();
|
new ClickOutSplitDialogPatch().Enable();
|
||||||
new ClickOutItemsListPatch().Enable();
|
new ClickOutItemsListPatch().Enable();
|
||||||
new ClickOutErrorScreenPatch().Enable();
|
new ClickOutErrorScreenPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DialogWindowPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.DialogWindow.Type, "Update");
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DialogWindowPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(object __instance, bool ___bool_0)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var instance = new R.DialogWindow(__instance);
|
||||||
|
|
||||||
|
if (!___bool_0)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(R.DialogWindow.Type, "Update");
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
||||||
public static void Postfix(object __instance, bool ___bool_0)
|
|
||||||
{
|
{
|
||||||
var instance = new R.DialogWindow(__instance);
|
instance.Accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!___bool_0)
|
public class ItemUiContextWindowPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Update));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(SplitDialog ___splitDialog_0, ItemsListWindow ____itemsListWindow)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
||||||
|
{
|
||||||
|
if (___splitDialog_0 != null && ___splitDialog_0.gameObject.activeSelf)
|
||||||
{
|
{
|
||||||
|
___splitDialog_0.Accept();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
if (____itemsListWindow.isActiveAndEnabled)
|
||||||
{
|
{
|
||||||
instance.Accept();
|
____itemsListWindow.Close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public class ItemUiContextWindowPatch : ModulePatch
|
|
||||||
{
|
public class ErrorScreenPatch : ModulePatch
|
||||||
protected override MethodBase GetTargetMethod()
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Update));
|
{
|
||||||
}
|
return AccessTools.Method(typeof(ErrorScreen), nameof(ErrorScreen.Update));
|
||||||
|
}
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(SplitDialog ___splitDialog_0, ItemsListWindow ____itemsListWindow)
|
[PatchPostfix]
|
||||||
{
|
public static void Postfix(ErrorScreen __instance)
|
||||||
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
{
|
||||||
{
|
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
||||||
if (___splitDialog_0 != null && ___splitDialog_0.gameObject.activeSelf)
|
{
|
||||||
{
|
__instance.method_4();
|
||||||
___splitDialog_0.Accept();
|
}
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (____itemsListWindow.isActiveAndEnabled)
|
public class AddOfferPatch : ModulePatch
|
||||||
{
|
{
|
||||||
____itemsListWindow.Close();
|
protected override MethodBase GetTargetMethod()
|
||||||
return;
|
{
|
||||||
}
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Update));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
[PatchPostfix]
|
||||||
|
public static void Postfix(AddOfferWindow __instance, InteractableElement ____addOfferButton)
|
||||||
public class ErrorScreenPatch : ModulePatch
|
{
|
||||||
{
|
if (!____addOfferButton.isActiveAndEnabled || !____addOfferButton.Interactable)
|
||||||
protected override MethodBase GetTargetMethod()
|
{
|
||||||
{
|
return;
|
||||||
return AccessTools.Method(typeof(ErrorScreen), nameof(ErrorScreen.Update));
|
}
|
||||||
}
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
||||||
[PatchPostfix]
|
{
|
||||||
public static void Postfix(ErrorScreen __instance)
|
__instance.method_1();
|
||||||
{
|
}
|
||||||
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
}
|
||||||
{
|
}
|
||||||
__instance.method_4();
|
|
||||||
}
|
public class ClickOutPatch : ModulePatch
|
||||||
}
|
{
|
||||||
}
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
public class AddOfferPatch : ModulePatch
|
return AccessTools.DeclaredMethod(typeof(MessageWindow), nameof(MessageWindow.Show));
|
||||||
{
|
}
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
[PatchPostfix]
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Update));
|
public static void Postfix(MessageWindow __instance)
|
||||||
}
|
{
|
||||||
|
if (!Settings.ClickOutOfDialogs.Value)
|
||||||
[PatchPostfix]
|
{
|
||||||
public static void Postfix(AddOfferWindow __instance, InteractableElement ____addOfferButton)
|
return;
|
||||||
{
|
}
|
||||||
if (!____addOfferButton.isActiveAndEnabled || !____addOfferButton.Interactable)
|
|
||||||
{
|
// Note the space after firewall, because unity doesn't trim names and BSG is incompetent.
|
||||||
return;
|
// Also for some reason some MessageWindows have a Window child and some don't.
|
||||||
}
|
Transform firewall = __instance.transform.Find("Firewall ") ?? __instance.transform.Find("Window/Firewall ");
|
||||||
|
Button button = firewall?.gameObject.GetOrAddComponent<Button>();
|
||||||
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter) || Input.GetKeyDown(KeyCode.Space))
|
if (button != null)
|
||||||
{
|
{
|
||||||
__instance.method_1();
|
button.transition = Selectable.Transition.None;
|
||||||
}
|
button.onClick.AddListener(__instance.Close);
|
||||||
}
|
__instance.R().UI.AddDisposable(button.onClick.RemoveAllListeners);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public class ClickOutPatch : ModulePatch
|
}
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
public class ClickOutSplitDialogPatch : ModulePatch
|
||||||
{
|
{
|
||||||
return AccessTools.DeclaredMethod(typeof(MessageWindow), nameof(MessageWindow.Show));
|
protected override MethodBase GetTargetMethod()
|
||||||
}
|
{
|
||||||
|
// Using method_0 because there's 2 Show(), and they have 10+ args and f that
|
||||||
[PatchPostfix]
|
return AccessTools.Method(typeof(SplitDialog), nameof(SplitDialog.method_0));
|
||||||
public static void Postfix(MessageWindow __instance)
|
}
|
||||||
{
|
|
||||||
if (!Settings.ClickOutOfDialogs.Value)
|
[PatchPostfix]
|
||||||
{
|
public static void Postfix(SplitDialog __instance)
|
||||||
return;
|
{
|
||||||
}
|
if (!Settings.ClickOutOfDialogs.Value)
|
||||||
|
{
|
||||||
// Note the space after firewall, because unity doesn't trim names and BSG is incompetent.
|
return;
|
||||||
// Also for some reason some MessageWindows have a Window child and some don't.
|
}
|
||||||
Transform firewall = __instance.transform.Find("Firewall ") ?? __instance.transform.Find("Window/Firewall ");
|
|
||||||
Button button = firewall?.gameObject.GetOrAddComponent<Button>();
|
Button button = __instance.transform.Find("Background")?.gameObject.GetOrAddComponent<Button>();
|
||||||
if (button != null)
|
if (button != null)
|
||||||
{
|
{
|
||||||
button.transition = Selectable.Transition.None;
|
button.transition = Selectable.Transition.None;
|
||||||
button.onClick.AddListener(__instance.Close);
|
button.onClick.RemoveAllListeners(); // There's no disposable here so keeping the listener count down
|
||||||
__instance.R().UI.AddDisposable(button.onClick.RemoveAllListeners);
|
button.onClick.AddListener(__instance.method_2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ClickOutSplitDialogPatch : ModulePatch
|
public class ClickOutItemsListPatch : ModulePatch
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
// Using method_0 because there's 2 Show(), and they have 10+ args and f that
|
return AccessTools.Method(typeof(ItemsListWindow), nameof(ItemsListWindow.Show));
|
||||||
return AccessTools.Method(typeof(SplitDialog), nameof(SplitDialog.method_0));
|
}
|
||||||
}
|
|
||||||
|
[PatchPostfix]
|
||||||
[PatchPostfix]
|
public static void Postfix(ItemsListWindow __instance)
|
||||||
public static void Postfix(SplitDialog __instance)
|
{
|
||||||
{
|
if (!Settings.ClickOutOfDialogs.Value)
|
||||||
if (!Settings.ClickOutOfDialogs.Value)
|
{
|
||||||
{
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
// Note the space after firewall, because unity doesn't trim names and BSG is incompetent
|
||||||
Button button = __instance.transform.Find("Background")?.gameObject.GetOrAddComponent<Button>();
|
Transform firewall = __instance.transform.Find("Firewall ");
|
||||||
if (button != null)
|
Button button = firewall?.gameObject.GetOrAddComponent<Button>();
|
||||||
{
|
if (button != null)
|
||||||
button.transition = Selectable.Transition.None;
|
{
|
||||||
button.onClick.RemoveAllListeners(); // There's no disposable here so keeping the listener count down
|
button.transition = Selectable.Transition.None;
|
||||||
button.onClick.AddListener(__instance.method_2);
|
button.onClick.AddListener(__instance.Close);
|
||||||
}
|
__instance.R().UI.AddDisposable(button.onClick.RemoveAllListeners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
public class ClickOutItemsListPatch : ModulePatch
|
|
||||||
{
|
public class ClickOutErrorScreenPatch : ModulePatch
|
||||||
protected override MethodBase GetTargetMethod()
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
return AccessTools.Method(typeof(ItemsListWindow), nameof(ItemsListWindow.Show));
|
{
|
||||||
}
|
return AccessTools.DeclaredMethod(typeof(ErrorScreen), nameof(ErrorScreen.Show));
|
||||||
|
}
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ItemsListWindow __instance)
|
[PatchPostfix]
|
||||||
{
|
public static void Postfix(ErrorScreen __instance)
|
||||||
if (!Settings.ClickOutOfDialogs.Value)
|
{
|
||||||
{
|
if (!Settings.ClickOutOfDialogs.Value)
|
||||||
return;
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
// Note the space after firewall, because unity doesn't trim names and BSG is incompetent
|
|
||||||
Transform firewall = __instance.transform.Find("Firewall ");
|
// Note the space after firewall, because unity doesn't trim names and BSG is incompetent
|
||||||
Button button = firewall?.gameObject.GetOrAddComponent<Button>();
|
Transform firewall = __instance.transform.Find("Firewall ");
|
||||||
if (button != null)
|
Button button = firewall?.gameObject.GetOrAddComponent<Button>();
|
||||||
{
|
if (button != null)
|
||||||
button.transition = Selectable.Transition.None;
|
{
|
||||||
button.onClick.AddListener(__instance.Close);
|
button.transition = Selectable.Transition.None;
|
||||||
__instance.R().UI.AddDisposable(button.onClick.RemoveAllListeners);
|
button.onClick.AddListener(__instance.method_4);
|
||||||
}
|
__instance.R().UI.AddDisposable(button.onClick.RemoveAllListeners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ClickOutErrorScreenPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.DeclaredMethod(typeof(ErrorScreen), nameof(ErrorScreen.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ErrorScreen __instance)
|
|
||||||
{
|
|
||||||
if (!Settings.ClickOutOfDialogs.Value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note the space after firewall, because unity doesn't trim names and BSG is incompetent
|
|
||||||
Transform firewall = __instance.transform.Find("Firewall ");
|
|
||||||
Button button = firewall?.gameObject.GetOrAddComponent<Button>();
|
|
||||||
if (button != null)
|
|
||||||
{
|
|
||||||
button.transition = Selectable.Transition.None;
|
|
||||||
button.onClick.AddListener(__instance.method_4);
|
|
||||||
__instance.R().UI.AddDisposable(button.onClick.RemoveAllListeners);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,512 +9,510 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UIFixes.ContextMenus;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class ContextMenuPatches
|
||||||
{
|
{
|
||||||
public static class ContextMenuPatches
|
private static InsuranceInteractions CurrentInsuranceInteractions = null;
|
||||||
|
private static RepairInteractions CurrentRepairInteractions = null;
|
||||||
|
private static string CreatedButtonInteractionId = null;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static InsuranceInteractions CurrentInsuranceInteractions = null;
|
new ContextMenuNamesPatch().Enable();
|
||||||
private static RepairInteractions CurrentRepairInteractions = null;
|
new PositionSubMenuPatch().Enable();
|
||||||
private static string CreatedButtonInteractionId = null;
|
new PositionInsuranceSubMenuPatch().Enable();
|
||||||
|
|
||||||
public static void Enable()
|
new DeclareSubInteractionsInventoryPatch().Enable();
|
||||||
|
new CreateSubInteractionsInventoryPatch().Enable();
|
||||||
|
|
||||||
|
new DeclareSubInteractionsTradingPatch().Enable();
|
||||||
|
new CreateSubInteractionsTradingPatch().Enable();
|
||||||
|
|
||||||
|
new SniffInteractionButtonCreationPatch().Enable();
|
||||||
|
new ChangeInteractionButtonCreationPatch().Enable();
|
||||||
|
|
||||||
|
new EnableInsureInnerItemsPatch().Enable();
|
||||||
|
|
||||||
|
new DisableLoadPresetOnBulletsPatch().Enable();
|
||||||
|
|
||||||
|
new EmptySlotMenuPatch().Enable();
|
||||||
|
new EmptySlotMenuRemovePatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContextMenuNamesPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new ContextMenuNamesPatch().Enable();
|
return AccessTools.Method(typeof(ContextMenuButton), nameof(ContextMenuButton.Show));
|
||||||
new PositionSubMenuPatch().Enable();
|
|
||||||
new PositionInsuranceSubMenuPatch().Enable();
|
|
||||||
|
|
||||||
new DeclareSubInteractionsInventoryPatch().Enable();
|
|
||||||
new CreateSubInteractionsInventoryPatch().Enable();
|
|
||||||
|
|
||||||
new DeclareSubInteractionsTradingPatch().Enable();
|
|
||||||
new CreateSubInteractionsTradingPatch().Enable();
|
|
||||||
|
|
||||||
new SniffInteractionButtonCreationPatch().Enable();
|
|
||||||
new ChangeInteractionButtonCreationPatch().Enable();
|
|
||||||
|
|
||||||
new EnableInsureInnerItemsPatch().Enable();
|
|
||||||
|
|
||||||
new DisableLoadPresetOnBulletsPatch().Enable();
|
|
||||||
|
|
||||||
new EmptySlotMenuPatch().Enable();
|
|
||||||
new EmptySlotMenuRemovePatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContextMenuNamesPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(string caption, TextMeshProUGUI ____text)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (MultiSelect.Count < 1)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ContextMenuButton), nameof(ContextMenuButton.Show));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (caption == EItemInfoButton.Insure.ToString())
|
||||||
public static void Postfix(string caption, TextMeshProUGUI ____text)
|
|
||||||
{
|
{
|
||||||
if (MultiSelect.Count < 1)
|
InsuranceCompanyClass insurance = ItemUiContext.Instance.Session.InsuranceCompany;
|
||||||
{
|
int count = MultiSelect.ItemContexts.Select(ic => InsuranceItem.FindOrCreate(ic.Item))
|
||||||
return;
|
.Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i))
|
||||||
}
|
.Count();
|
||||||
|
|
||||||
if (caption == EItemInfoButton.Insure.ToString())
|
if (count > 0)
|
||||||
{
|
{
|
||||||
InsuranceCompanyClass insurance = ItemUiContext.Instance.Session.InsuranceCompany;
|
____text.text += " (x" + count + ")";
|
||||||
int count = MultiSelect.ItemContexts.Select(ic => InsuranceItem.FindOrCreate(ic.Item))
|
|
||||||
.Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i))
|
|
||||||
.Count();
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (caption == EItemInfoButton.Equip.ToString())
|
|
||||||
{
|
|
||||||
int count = MultiSelect.InteractionCount(EItemInfoButton.Equip, ItemUiContext.Instance);
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (caption == EItemInfoButton.Unequip.ToString())
|
|
||||||
{
|
|
||||||
int count = MultiSelect.InteractionCount(EItemInfoButton.Unequip, ItemUiContext.Instance);
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (caption == EItemInfoButton.LoadAmmo.ToString())
|
|
||||||
{
|
|
||||||
int count = MultiSelect.InteractionCount(EItemInfoButton.LoadAmmo, ItemUiContext.Instance);
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (caption == EItemInfoButton.UnloadAmmo.ToString())
|
|
||||||
{
|
|
||||||
int count = MultiSelect.InteractionCount(EItemInfoButton.UnloadAmmo, ItemUiContext.Instance);
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (caption == EItemInfoButton.ApplyMagPreset.ToString())
|
|
||||||
{
|
|
||||||
int count = MultiSelect.InteractionCount(EItemInfoButton.ApplyMagPreset, ItemUiContext.Instance);
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (caption == EItemInfoButton.Unpack.ToString())
|
|
||||||
{
|
|
||||||
int count = MultiSelect.InteractionCount(EItemInfoButton.Unpack, ItemUiContext.Instance);
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
____text.text += " (x" + count + ")";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (caption == EItemInfoButton.Equip.ToString())
|
||||||
|
|
||||||
public class DeclareSubInteractionsInventoryPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
return AccessTools.Method(R.InventoryInteractions.Type, "get_SubInteractions");
|
int count = MultiSelect.InteractionCount(EItemInfoButton.Equip, ItemUiContext.Instance);
|
||||||
}
|
if (count > 0)
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
|
||||||
{
|
|
||||||
__result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateSubInteractionsInventoryPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static bool LoadingInsuranceActions = false;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, Item ___item_0, ItemUiContext ___itemUiContext_1)
|
|
||||||
{
|
|
||||||
// Clear this, since something else should be active (even a different mouseover of the insurance button)
|
|
||||||
LoadingInsuranceActions = false;
|
|
||||||
|
|
||||||
if (parentInteraction == EItemInfoButton.Insure)
|
|
||||||
{
|
{
|
||||||
int playerRubles = GetPlayerRubles(___itemUiContext_1);
|
____text.text += " (x" + count + ")";
|
||||||
|
|
||||||
CurrentInsuranceInteractions = MultiSelect.Active ?
|
|
||||||
new(MultiSelect.ItemContexts.Select(ic => ic.Item), ___itemUiContext_1, playerRubles) :
|
|
||||||
new(___item_0, ___itemUiContext_1, playerRubles);
|
|
||||||
|
|
||||||
// Because this is async, need to protect against a different subInteractions activating before loading is done
|
|
||||||
// This isn't thread-safe at all but now the race condition is a microsecond instead of hundreds of milliseconds.
|
|
||||||
LoadingInsuranceActions = true;
|
|
||||||
CurrentInsuranceInteractions.LoadAsync(() =>
|
|
||||||
{
|
|
||||||
if (LoadingInsuranceActions)
|
|
||||||
{
|
|
||||||
subInteractionsWrapper.SetSubInteractions(CurrentInsuranceInteractions);
|
|
||||||
LoadingInsuranceActions = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentInteraction == EItemInfoButton.Repair)
|
|
||||||
{
|
|
||||||
int playerRubles = GetPlayerRubles(___itemUiContext_1);
|
|
||||||
|
|
||||||
CurrentRepairInteractions = new(___item_0, ___itemUiContext_1, playerRubles);
|
|
||||||
subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DeclareSubInteractionsTradingPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(R.TradingInteractions.Type, "get_SubInteractions");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
|
||||||
{
|
|
||||||
__result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateSubInteractionsTradingPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static bool LoadingInsuranceActions = false;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(R.TradingInteractions.Type, "CreateSubInteractions");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(object __instance, EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, ItemUiContext ___itemUiContext_0)
|
|
||||||
{
|
|
||||||
// Clear this, since something else should be active (even a different mouseover of the insurance button)
|
|
||||||
LoadingInsuranceActions = false;
|
|
||||||
|
|
||||||
var wrappedInstance = new R.TradingInteractions(__instance);
|
|
||||||
|
|
||||||
if (parentInteraction == EItemInfoButton.Insure)
|
|
||||||
{
|
|
||||||
int playerRubles = GetPlayerRubles(___itemUiContext_0);
|
|
||||||
|
|
||||||
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054
|
|
||||||
Item item = wrappedInstance.Item;
|
|
||||||
|
|
||||||
CurrentInsuranceInteractions = new(item, ___itemUiContext_0, playerRubles);
|
|
||||||
CurrentInsuranceInteractions = MultiSelect.Active ?
|
|
||||||
new(MultiSelect.ItemContexts.Select(ic => ic.Item), ___itemUiContext_0, playerRubles) :
|
|
||||||
new(item, ___itemUiContext_0, playerRubles);
|
|
||||||
|
|
||||||
// Because this is async, need to protect against a different subInteractions activating before loading is done
|
|
||||||
// This isn't thread-safe at all but now the race condition is a microsecond instead of hundreds of milliseconds.
|
|
||||||
LoadingInsuranceActions = true;
|
|
||||||
CurrentInsuranceInteractions.LoadAsync(() =>
|
|
||||||
{
|
|
||||||
if (LoadingInsuranceActions)
|
|
||||||
{
|
|
||||||
subInteractionsWrapper.SetSubInteractions(CurrentInsuranceInteractions);
|
|
||||||
LoadingInsuranceActions = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parentInteraction == EItemInfoButton.Repair)
|
|
||||||
{
|
|
||||||
int playerRubles = GetPlayerRubles(___itemUiContext_0);
|
|
||||||
|
|
||||||
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054
|
|
||||||
Item item = wrappedInstance.Item;
|
|
||||||
|
|
||||||
CurrentRepairInteractions = new(item, ___itemUiContext_0, playerRubles);
|
|
||||||
subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SniffInteractionButtonCreationPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(InteractionButtonsContainer), nameof(InteractionButtonsContainer.method_3));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(DynamicInteractionClass interaction)
|
|
||||||
{
|
|
||||||
if (interaction.IsInsuranceInteraction() || interaction.IsRepairInteraction())
|
|
||||||
{
|
|
||||||
CreatedButtonInteractionId = interaction.Id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (caption == EItemInfoButton.Unequip.ToString())
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
{
|
||||||
CreatedButtonInteractionId = null;
|
int count = MultiSelect.InteractionCount(EItemInfoButton.Unequip, ItemUiContext.Instance);
|
||||||
}
|
if (count > 0)
|
||||||
}
|
|
||||||
|
|
||||||
public class ChangeInteractionButtonCreationPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(InteractionButtonsContainer), nameof(InteractionButtonsContainer.method_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(SimpleContextMenuButton button)
|
|
||||||
{
|
|
||||||
if (!String.IsNullOrEmpty(CreatedButtonInteractionId))
|
|
||||||
{
|
{
|
||||||
if (InsuranceInteractions.IsInsuranceInteractionId(CreatedButtonInteractionId) && CurrentInsuranceInteractions != null)
|
____text.text += " (x" + count + ")";
|
||||||
{
|
|
||||||
button.SetButtonInteraction(CurrentInsuranceInteractions.GetButtonInteraction(CreatedButtonInteractionId));
|
|
||||||
}
|
|
||||||
else if (RepairInteractions.IsRepairInteractionId(CreatedButtonInteractionId) && CurrentRepairInteractions != null)
|
|
||||||
{
|
|
||||||
button.SetButtonInteraction(CurrentRepairInteractions.GetButtonInteraction(CreatedButtonInteractionId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (caption == EItemInfoButton.LoadAmmo.ToString())
|
||||||
|
|
||||||
public class CleanUpInteractionsPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(SimpleContextMenu), nameof(SimpleContextMenu.Close));
|
int count = MultiSelect.InteractionCount(EItemInfoButton.LoadAmmo, ItemUiContext.Instance);
|
||||||
}
|
if (count > 0)
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
CurrentInsuranceInteractions = null;
|
|
||||||
CurrentRepairInteractions = null;
|
|
||||||
CreatedButtonInteractionId = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EnableInsureInnerItemsPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(R.ContextMenuHelper.Type, "IsInteractive");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(object __instance, EItemInfoButton button, ref IResult __result, Item ___item_0)
|
|
||||||
{
|
|
||||||
if (button != EItemInfoButton.Insure)
|
|
||||||
{
|
{
|
||||||
return true;
|
____text.text += " (x" + count + ")";
|
||||||
}
|
|
||||||
|
|
||||||
InsuranceCompanyClass insurance = new R.ContextMenuHelper(__instance).InsuranceCompany;
|
|
||||||
|
|
||||||
IEnumerable<Item> items = MultiSelect.Active ? MultiSelect.ItemContexts.Select(ic => ic.Item) : [___item_0];
|
|
||||||
IEnumerable<InsuranceItem> InsuranceItemes = items.Select(InsuranceItem.FindOrCreate);
|
|
||||||
IEnumerable<InsuranceItem> insurableItems = InsuranceItemes.SelectMany(insurance.GetItemChildren)
|
|
||||||
.Flatten(insurance.GetItemChildren)
|
|
||||||
.Concat(InsuranceItemes)
|
|
||||||
.Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i));
|
|
||||||
|
|
||||||
if (insurableItems.Any())
|
|
||||||
{
|
|
||||||
__result = SuccessfulResult.New;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DisableLoadPresetOnBulletsPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(MagazineBuildClass), nameof(MagazineBuildClass.TryFindPresetSource));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(Item selectedItem, ref GStruct416<Item> __result)
|
|
||||||
{
|
|
||||||
if (Settings.LoadMagPresetOnBullets.Value)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedItem is BulletClass)
|
|
||||||
{
|
|
||||||
__result = new MagazineBuildClass.Class3183(selectedItem);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EmptySlotMenuPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.DeclaredMethod(typeof(ModSlotView), nameof(ModSlotView.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ModSlotView __instance, Slot slot, ItemContextAbstractClass parentItemContext, ItemUiContext itemUiContext)
|
|
||||||
{
|
|
||||||
if (!Settings.EnableSlotSearch.Value || slot.ContainedItem != null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EmptySlotMenuTrigger menuTrigger = __instance.GetOrAddComponent<EmptySlotMenuTrigger>();
|
|
||||||
menuTrigger.Init(slot, parentItemContext, itemUiContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EmptySlotMenuRemovePatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.DeclaredMethod(typeof(ModSlotView), nameof(ModSlotView.SetupItemView));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ModSlotView __instance)
|
|
||||||
{
|
|
||||||
EmptySlotMenuTrigger menuTrigger = __instance.GetComponent<EmptySlotMenuTrigger>();
|
|
||||||
if (menuTrigger != null)
|
|
||||||
{
|
|
||||||
UnityEngine.Object.Destroy(menuTrigger);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (caption == EItemInfoButton.UnloadAmmo.ToString())
|
||||||
|
|
||||||
public class PositionSubMenuPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions");
|
int count = MultiSelect.InteractionCount(EItemInfoButton.UnloadAmmo, ItemUiContext.Instance);
|
||||||
}
|
if (count > 0)
|
||||||
|
|
||||||
// Existing logic tries to place it on the right, moving to the left if necessary. They didn't do it correctly, so it always goes on the left.
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ISubInteractions subInteractionsWrapper)
|
|
||||||
{
|
|
||||||
if (subInteractionsWrapper is not InteractionButtonsContainer buttonsContainer)
|
|
||||||
{
|
{
|
||||||
return;
|
____text.text += " (x" + count + ")";
|
||||||
}
|
|
||||||
|
|
||||||
var wrappedContainer = buttonsContainer.R();
|
|
||||||
SimpleContextMenuButton button = wrappedContainer.ContextMenuButton;
|
|
||||||
SimpleContextMenu flyoutMenu = wrappedContainer.ContextMenu;
|
|
||||||
|
|
||||||
if (button == null || flyoutMenu == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PositionContextMenuFlyout(button, flyoutMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insurance submenu is async, need to postfix the actual set call
|
|
||||||
public class PositionInsuranceSubMenuPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(InteractionButtonsContainer), nameof(InteractionButtonsContainer.SetSubInteractions)).MakeGenericMethod([typeof(InsuranceInteractions.EInsurers)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Existing logic tries to place it on the right, moving to the left if necessary. They didn't do it correctly, so it always goes on the left.
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(SimpleContextMenuButton ___simpleContextMenuButton_0, SimpleContextMenu ___simpleContextMenu_0)
|
|
||||||
{
|
|
||||||
PositionContextMenuFlyout(___simpleContextMenuButton_0, ___simpleContextMenu_0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PositionContextMenuFlyout(SimpleContextMenuButton button, SimpleContextMenu flyoutMenu)
|
|
||||||
{
|
|
||||||
RectTransform buttonTransform = button.RectTransform();
|
|
||||||
RectTransform flyoutTransform = flyoutMenu.RectTransform();
|
|
||||||
|
|
||||||
Vector2 leftPosition = flyoutTransform.position; // BSG's code will always put it on the left
|
|
||||||
leftPosition = new Vector2((float)Math.Round((double)leftPosition.x), (float)Math.Round((double)leftPosition.y));
|
|
||||||
|
|
||||||
Vector2 size = buttonTransform.rect.size;
|
|
||||||
Vector2 rightPosition = size - size * buttonTransform.pivot;
|
|
||||||
rightPosition = buttonTransform.TransformPoint(rightPosition);
|
|
||||||
|
|
||||||
// Round vector the way that CorrectPosition does
|
|
||||||
rightPosition = new Vector2((float)Math.Round((double)rightPosition.x), (float)Math.Round((double)rightPosition.y));
|
|
||||||
|
|
||||||
if (Settings.ContextMenuOnRight.Value)
|
|
||||||
{
|
|
||||||
// Try on the right
|
|
||||||
flyoutTransform.position = rightPosition;
|
|
||||||
flyoutMenu.CorrectPosition();
|
|
||||||
|
|
||||||
// This means CorrectPosition() moved it
|
|
||||||
if (!(flyoutTransform.position.x - rightPosition.x).IsZero())
|
|
||||||
{
|
|
||||||
flyoutTransform.position = leftPosition;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (caption == EItemInfoButton.ApplyMagPreset.ToString())
|
||||||
{
|
{
|
||||||
flyoutTransform.position = leftPosition;
|
int count = MultiSelect.InteractionCount(EItemInfoButton.ApplyMagPreset, ItemUiContext.Instance);
|
||||||
flyoutMenu.CorrectPosition();
|
if (count > 0)
|
||||||
|
|
||||||
if (!(flyoutTransform.position.x - leftPosition.x).IsZero())
|
|
||||||
{
|
{
|
||||||
flyoutTransform.position = rightPosition;
|
____text.text += " (x" + count + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (caption == EItemInfoButton.Unpack.ToString())
|
||||||
|
|
||||||
private static int GetPlayerRubles(ItemUiContext itemUiContext)
|
|
||||||
{
|
|
||||||
StashClass stash = itemUiContext.R().InventoryController.Inventory.Stash;
|
|
||||||
if (stash == null)
|
|
||||||
{
|
{
|
||||||
return 0;
|
int count = MultiSelect.InteractionCount(EItemInfoButton.Unpack, ItemUiContext.Instance);
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
____text.text += " (x" + count + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.Money.GetMoneySums(stash.Grid.ContainedItems.Keys)[ECurrencyType.RUB];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DeclareSubInteractionsInventoryPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.InventoryInteractions.Type, "get_SubInteractions");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
||||||
|
{
|
||||||
|
__result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateSubInteractionsInventoryPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static bool LoadingInsuranceActions = false;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, Item ___item_0, ItemUiContext ___itemUiContext_1)
|
||||||
|
{
|
||||||
|
// Clear this, since something else should be active (even a different mouseover of the insurance button)
|
||||||
|
LoadingInsuranceActions = false;
|
||||||
|
|
||||||
|
if (parentInteraction == EItemInfoButton.Insure)
|
||||||
|
{
|
||||||
|
int playerRubles = GetPlayerRubles(___itemUiContext_1);
|
||||||
|
|
||||||
|
CurrentInsuranceInteractions = MultiSelect.Active ?
|
||||||
|
new(MultiSelect.ItemContexts.Select(ic => ic.Item), ___itemUiContext_1, playerRubles) :
|
||||||
|
new(___item_0, ___itemUiContext_1, playerRubles);
|
||||||
|
|
||||||
|
// Because this is async, need to protect against a different subInteractions activating before loading is done
|
||||||
|
// This isn't thread-safe at all but now the race condition is a microsecond instead of hundreds of milliseconds.
|
||||||
|
LoadingInsuranceActions = true;
|
||||||
|
CurrentInsuranceInteractions.LoadAsync(() =>
|
||||||
|
{
|
||||||
|
if (LoadingInsuranceActions)
|
||||||
|
{
|
||||||
|
subInteractionsWrapper.SetSubInteractions(CurrentInsuranceInteractions);
|
||||||
|
LoadingInsuranceActions = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentInteraction == EItemInfoButton.Repair)
|
||||||
|
{
|
||||||
|
int playerRubles = GetPlayerRubles(___itemUiContext_1);
|
||||||
|
|
||||||
|
CurrentRepairInteractions = new(___item_0, ___itemUiContext_1, playerRubles);
|
||||||
|
subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeclareSubInteractionsTradingPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.TradingInteractions.Type, "get_SubInteractions");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
||||||
|
{
|
||||||
|
__result = __result.Append(EItemInfoButton.Repair).Append(EItemInfoButton.Insure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreateSubInteractionsTradingPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static bool LoadingInsuranceActions = false;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.TradingInteractions.Type, "CreateSubInteractions");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(object __instance, EItemInfoButton parentInteraction, ISubInteractions subInteractionsWrapper, ItemUiContext ___itemUiContext_0)
|
||||||
|
{
|
||||||
|
// Clear this, since something else should be active (even a different mouseover of the insurance button)
|
||||||
|
LoadingInsuranceActions = false;
|
||||||
|
|
||||||
|
var wrappedInstance = new R.TradingInteractions(__instance);
|
||||||
|
|
||||||
|
if (parentInteraction == EItemInfoButton.Insure)
|
||||||
|
{
|
||||||
|
int playerRubles = GetPlayerRubles(___itemUiContext_0);
|
||||||
|
|
||||||
|
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054
|
||||||
|
Item item = wrappedInstance.Item;
|
||||||
|
|
||||||
|
CurrentInsuranceInteractions = new(item, ___itemUiContext_0, playerRubles);
|
||||||
|
CurrentInsuranceInteractions = MultiSelect.Active ?
|
||||||
|
new(MultiSelect.ItemContexts.Select(ic => ic.Item), ___itemUiContext_0, playerRubles) :
|
||||||
|
new(item, ___itemUiContext_0, playerRubles);
|
||||||
|
|
||||||
|
// Because this is async, need to protect against a different subInteractions activating before loading is done
|
||||||
|
// This isn't thread-safe at all but now the race condition is a microsecond instead of hundreds of milliseconds.
|
||||||
|
LoadingInsuranceActions = true;
|
||||||
|
CurrentInsuranceInteractions.LoadAsync(() =>
|
||||||
|
{
|
||||||
|
if (LoadingInsuranceActions)
|
||||||
|
{
|
||||||
|
subInteractionsWrapper.SetSubInteractions(CurrentInsuranceInteractions);
|
||||||
|
LoadingInsuranceActions = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentInteraction == EItemInfoButton.Repair)
|
||||||
|
{
|
||||||
|
int playerRubles = GetPlayerRubles(___itemUiContext_0);
|
||||||
|
|
||||||
|
// CreateSubInteractions is only on the base class here, which doesn't have an Item. But __instance is actually a GClass3054
|
||||||
|
Item item = wrappedInstance.Item;
|
||||||
|
|
||||||
|
CurrentRepairInteractions = new(item, ___itemUiContext_0, playerRubles);
|
||||||
|
subInteractionsWrapper.SetSubInteractions(CurrentRepairInteractions);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SniffInteractionButtonCreationPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(InteractionButtonsContainer), nameof(InteractionButtonsContainer.method_3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(DynamicInteractionClass interaction)
|
||||||
|
{
|
||||||
|
if (interaction.IsInsuranceInteraction() || interaction.IsRepairInteraction())
|
||||||
|
{
|
||||||
|
CreatedButtonInteractionId = interaction.Id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
CreatedButtonInteractionId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangeInteractionButtonCreationPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(InteractionButtonsContainer), nameof(InteractionButtonsContainer.method_5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(SimpleContextMenuButton button)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(CreatedButtonInteractionId))
|
||||||
|
{
|
||||||
|
if (InsuranceInteractions.IsInsuranceInteractionId(CreatedButtonInteractionId) && CurrentInsuranceInteractions != null)
|
||||||
|
{
|
||||||
|
button.SetButtonInteraction(CurrentInsuranceInteractions.GetButtonInteraction(CreatedButtonInteractionId));
|
||||||
|
}
|
||||||
|
else if (RepairInteractions.IsRepairInteractionId(CreatedButtonInteractionId) && CurrentRepairInteractions != null)
|
||||||
|
{
|
||||||
|
button.SetButtonInteraction(CurrentRepairInteractions.GetButtonInteraction(CreatedButtonInteractionId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CleanUpInteractionsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(SimpleContextMenu), nameof(SimpleContextMenu.Close));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
CurrentInsuranceInteractions = null;
|
||||||
|
CurrentRepairInteractions = null;
|
||||||
|
CreatedButtonInteractionId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EnableInsureInnerItemsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.ContextMenuHelper.Type, "IsInteractive");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(object __instance, EItemInfoButton button, ref IResult __result, Item ___item_0)
|
||||||
|
{
|
||||||
|
if (button != EItemInfoButton.Insure)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsuranceCompanyClass insurance = new R.ContextMenuHelper(__instance).InsuranceCompany;
|
||||||
|
|
||||||
|
IEnumerable<Item> items = MultiSelect.Active ? MultiSelect.ItemContexts.Select(ic => ic.Item) : [___item_0];
|
||||||
|
IEnumerable<InsuranceItem> InsuranceItemes = items.Select(InsuranceItem.FindOrCreate);
|
||||||
|
IEnumerable<InsuranceItem> insurableItems = InsuranceItemes.SelectMany(insurance.GetItemChildren)
|
||||||
|
.Flatten(insurance.GetItemChildren)
|
||||||
|
.Concat(InsuranceItemes)
|
||||||
|
.Where(i => insurance.ItemTypeAvailableForInsurance(i) && !insurance.InsuredItems.Contains(i));
|
||||||
|
|
||||||
|
if (insurableItems.Any())
|
||||||
|
{
|
||||||
|
__result = SuccessfulResult.New;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DisableLoadPresetOnBulletsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(MagazineBuildClass), nameof(MagazineBuildClass.TryFindPresetSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(Item selectedItem, ref GStruct416<Item> __result)
|
||||||
|
{
|
||||||
|
if (Settings.LoadMagPresetOnBullets.Value)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedItem is BulletClass)
|
||||||
|
{
|
||||||
|
__result = new MagazineBuildClass.Class3183(selectedItem);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EmptySlotMenuPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.DeclaredMethod(typeof(ModSlotView), nameof(ModSlotView.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ModSlotView __instance, Slot slot, ItemContextAbstractClass parentItemContext, ItemUiContext itemUiContext)
|
||||||
|
{
|
||||||
|
if (!Settings.EnableSlotSearch.Value || slot.ContainedItem != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmptySlotMenuTrigger menuTrigger = __instance.GetOrAddComponent<EmptySlotMenuTrigger>();
|
||||||
|
menuTrigger.Init(slot, parentItemContext, itemUiContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EmptySlotMenuRemovePatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.DeclaredMethod(typeof(ModSlotView), nameof(ModSlotView.SetupItemView));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ModSlotView __instance)
|
||||||
|
{
|
||||||
|
EmptySlotMenuTrigger menuTrigger = __instance.GetComponent<EmptySlotMenuTrigger>();
|
||||||
|
if (menuTrigger != null)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(menuTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PositionSubMenuPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(R.InventoryInteractions.Type, "CreateSubInteractions");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Existing logic tries to place it on the right, moving to the left if necessary. They didn't do it correctly, so it always goes on the left.
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ISubInteractions subInteractionsWrapper)
|
||||||
|
{
|
||||||
|
if (subInteractionsWrapper is not InteractionButtonsContainer buttonsContainer)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrappedContainer = buttonsContainer.R();
|
||||||
|
SimpleContextMenuButton button = wrappedContainer.ContextMenuButton;
|
||||||
|
SimpleContextMenu flyoutMenu = wrappedContainer.ContextMenu;
|
||||||
|
|
||||||
|
if (button == null || flyoutMenu == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionContextMenuFlyout(button, flyoutMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insurance submenu is async, need to postfix the actual set call
|
||||||
|
public class PositionInsuranceSubMenuPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(InteractionButtonsContainer), nameof(InteractionButtonsContainer.SetSubInteractions)).MakeGenericMethod([typeof(InsuranceInteractions.EInsurers)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Existing logic tries to place it on the right, moving to the left if necessary. They didn't do it correctly, so it always goes on the left.
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(SimpleContextMenuButton ___simpleContextMenuButton_0, SimpleContextMenu ___simpleContextMenu_0)
|
||||||
|
{
|
||||||
|
PositionContextMenuFlyout(___simpleContextMenuButton_0, ___simpleContextMenu_0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PositionContextMenuFlyout(SimpleContextMenuButton button, SimpleContextMenu flyoutMenu)
|
||||||
|
{
|
||||||
|
RectTransform buttonTransform = button.RectTransform();
|
||||||
|
RectTransform flyoutTransform = flyoutMenu.RectTransform();
|
||||||
|
|
||||||
|
Vector2 leftPosition = flyoutTransform.position; // BSG's code will always put it on the left
|
||||||
|
leftPosition = new Vector2((float)Math.Round((double)leftPosition.x), (float)Math.Round((double)leftPosition.y));
|
||||||
|
|
||||||
|
Vector2 size = buttonTransform.rect.size;
|
||||||
|
Vector2 rightPosition = size - size * buttonTransform.pivot;
|
||||||
|
rightPosition = buttonTransform.TransformPoint(rightPosition);
|
||||||
|
|
||||||
|
// Round vector the way that CorrectPosition does
|
||||||
|
rightPosition = new Vector2((float)Math.Round((double)rightPosition.x), (float)Math.Round((double)rightPosition.y));
|
||||||
|
|
||||||
|
if (Settings.ContextMenuOnRight.Value)
|
||||||
|
{
|
||||||
|
// Try on the right
|
||||||
|
flyoutTransform.position = rightPosition;
|
||||||
|
flyoutMenu.CorrectPosition();
|
||||||
|
|
||||||
|
// This means CorrectPosition() moved it
|
||||||
|
if (!(flyoutTransform.position.x - rightPosition.x).IsZero())
|
||||||
|
{
|
||||||
|
flyoutTransform.position = leftPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flyoutTransform.position = leftPosition;
|
||||||
|
flyoutMenu.CorrectPosition();
|
||||||
|
|
||||||
|
if (!(flyoutTransform.position.x - leftPosition.x).IsZero())
|
||||||
|
{
|
||||||
|
flyoutTransform.position = rightPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetPlayerRubles(ItemUiContext itemUiContext)
|
||||||
|
{
|
||||||
|
StashClass stash = itemUiContext.R().InventoryController.Inventory.Stash;
|
||||||
|
if (stash == null)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return R.Money.GetMoneySums(stash.Grid.ContainedItems.Keys)[ECurrencyType.RUB];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,217 +8,216 @@ using TMPro;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class ContextMenuShortcutPatches
|
||||||
{
|
{
|
||||||
public static class ContextMenuShortcutPatches
|
private static TMP_InputField LastSelectedInput = null;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static TMP_InputField LastSelectedInput = null;
|
new ItemUiContextPatch().Enable();
|
||||||
|
|
||||||
public static void Enable()
|
new HideoutItemViewRegisterContextPatch().Enable();
|
||||||
|
new HideoutItemViewUnegisterContextPatch().Enable();
|
||||||
|
|
||||||
|
new TradingPanelRegisterContextPatch().Enable();
|
||||||
|
new TradingPanelUnregisterContextPatch().Enable();
|
||||||
|
|
||||||
|
new SelectCurrentContextPatch().Enable();
|
||||||
|
new DeselectCurrentContextPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ItemUiContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static ItemInfoInteractionsAbstractClass<EItemInfoButton> Interactions;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new ItemUiContextPatch().Enable();
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Update));
|
||||||
|
|
||||||
new HideoutItemViewRegisterContextPatch().Enable();
|
|
||||||
new HideoutItemViewUnegisterContextPatch().Enable();
|
|
||||||
|
|
||||||
new TradingPanelRegisterContextPatch().Enable();
|
|
||||||
new TradingPanelUnregisterContextPatch().Enable();
|
|
||||||
|
|
||||||
new SelectCurrentContextPatch().Enable();
|
|
||||||
new DeselectCurrentContextPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ItemUiContextPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemUiContext __instance)
|
||||||
{
|
{
|
||||||
private static ItemInfoInteractionsAbstractClass<EItemInfoButton> Interactions;
|
// Need an item context to operate on
|
||||||
|
ItemContextAbstractClass itemContext = __instance.R().ItemContext;
|
||||||
protected override MethodBase GetTargetMethod()
|
if (itemContext == null)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Update));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (!Settings.ItemContextBlocksTextInputs.Value &&
|
||||||
public static void Postfix(ItemUiContext __instance)
|
EventSystem.current?.currentSelectedGameObject != null &&
|
||||||
|
EventSystem.current.currentSelectedGameObject.GetComponent<TMP_InputField>() != null)
|
||||||
{
|
{
|
||||||
// Need an item context to operate on
|
return;
|
||||||
ItemContextAbstractClass itemContext = __instance.R().ItemContext;
|
|
||||||
if (itemContext == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Settings.ItemContextBlocksTextInputs.Value &&
|
|
||||||
EventSystem.current?.currentSelectedGameObject != null &&
|
|
||||||
EventSystem.current.currentSelectedGameObject.GetComponent<TMP_InputField>() != null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.InspectKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.Inspect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.OpenKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.Open);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.TopUpKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.TopUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.UseKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.Use);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.UseAllKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.UseAll, EItemInfoButton.Use);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.UnloadKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.Unload, EItemInfoButton.UnloadAmmo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.UnpackKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.Unpack);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.FilterByKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.FilterSearch);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.LinkedSearchKeyBind.Value.IsDown())
|
|
||||||
{
|
|
||||||
TryInteraction(__instance, itemContext, EItemInfoButton.LinkedSearch);
|
|
||||||
}
|
|
||||||
|
|
||||||
Interactions = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TryInteraction(ItemUiContext itemUiContext, ItemContextAbstractClass itemContext, EItemInfoButton interaction, EItemInfoButton? fallbackInteraction = null)
|
if (Settings.InspectKeyBind.Value.IsDown())
|
||||||
{
|
{
|
||||||
Interactions ??= itemUiContext.GetItemContextInteractions(itemContext, null);
|
TryInteraction(__instance, itemContext, EItemInfoButton.Inspect);
|
||||||
if (!Interactions.ExecuteInteraction(interaction) && fallbackInteraction.HasValue)
|
|
||||||
{
|
|
||||||
Interactions.ExecuteInteraction(fallbackInteraction.Value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Settings.OpenKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.Open);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.TopUpKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.TopUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.UseKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.Use);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.UseAllKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.UseAll, EItemInfoButton.Use);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.UnloadKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.Unload, EItemInfoButton.UnloadAmmo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.UnpackKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.Unpack);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.FilterByKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.FilterSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.LinkedSearchKeyBind.Value.IsDown())
|
||||||
|
{
|
||||||
|
TryInteraction(__instance, itemContext, EItemInfoButton.LinkedSearch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Interactions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// HideoutItemViews don't register themselves with ItemUiContext for some reason
|
private static void TryInteraction(ItemUiContext itemUiContext, ItemContextAbstractClass itemContext, EItemInfoButton interaction, EItemInfoButton? fallbackInteraction = null)
|
||||||
public class HideoutItemViewRegisterContextPatch : ModulePatch
|
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
Interactions ??= itemUiContext.GetItemContextInteractions(itemContext, null);
|
||||||
|
if (!Interactions.ExecuteInteraction(interaction) && fallbackInteraction.HasValue)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(HideoutItemView), nameof(HideoutItemView.OnPointerEnter));
|
Interactions.ExecuteInteraction(fallbackInteraction.Value);
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(HideoutItemView __instance, ItemUiContext ___ItemUiContext)
|
|
||||||
{
|
|
||||||
___ItemUiContext.RegisterCurrentItemContext(__instance.ItemContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class HideoutItemViewUnegisterContextPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(HideoutItemView), nameof(HideoutItemView.OnPointerExit));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(HideoutItemView __instance, ItemUiContext ___ItemUiContext)
|
|
||||||
{
|
|
||||||
___ItemUiContext.UnregisterCurrentItemContext(__instance.ItemContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TradingPanelRegisterContextPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(TradingRequisitePanel), nameof(TradingRequisitePanel.method_1)); // OnHoverStart
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ItemUiContext ___itemUiContext_0, ItemContextAbstractClass ___itemContextAbstractClass)
|
|
||||||
{
|
|
||||||
___itemUiContext_0.RegisterCurrentItemContext(___itemContextAbstractClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TradingPanelUnregisterContextPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(TradingRequisitePanel), nameof(TradingRequisitePanel.method_2)); // OnHoverEnd
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ItemUiContext ___itemUiContext_0, ItemContextAbstractClass ___itemContextAbstractClass)
|
|
||||||
{
|
|
||||||
___itemUiContext_0.UnregisterCurrentItemContext(___itemContextAbstractClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keybinds don't work if a textbox has focus - setting the textbox to readonly seems the best way to fix this
|
|
||||||
// without changing selection and causing weird side effects
|
|
||||||
public class SelectCurrentContextPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.RegisterCurrentItemContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
if (!Settings.ItemContextBlocksTextInputs.Value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EventSystem.current?.currentSelectedGameObject != null)
|
|
||||||
{
|
|
||||||
LastSelectedInput = EventSystem.current.currentSelectedGameObject.GetComponent<TMP_InputField>();
|
|
||||||
if (LastSelectedInput != null)
|
|
||||||
{
|
|
||||||
LastSelectedInput.readOnly = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DeselectCurrentContextPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.UnregisterCurrentItemContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
if (!Settings.ItemContextBlocksTextInputs.Value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LastSelectedInput != null)
|
|
||||||
{
|
|
||||||
LastSelectedInput.readOnly = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LastSelectedInput = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HideoutItemViews don't register themselves with ItemUiContext for some reason
|
||||||
|
public class HideoutItemViewRegisterContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(HideoutItemView), nameof(HideoutItemView.OnPointerEnter));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(HideoutItemView __instance, ItemUiContext ___ItemUiContext)
|
||||||
|
{
|
||||||
|
___ItemUiContext.RegisterCurrentItemContext(__instance.ItemContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HideoutItemViewUnegisterContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(HideoutItemView), nameof(HideoutItemView.OnPointerExit));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(HideoutItemView __instance, ItemUiContext ___ItemUiContext)
|
||||||
|
{
|
||||||
|
___ItemUiContext.UnregisterCurrentItemContext(__instance.ItemContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TradingPanelRegisterContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(TradingRequisitePanel), nameof(TradingRequisitePanel.method_1)); // OnHoverStart
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemUiContext ___itemUiContext_0, ItemContextAbstractClass ___itemContextAbstractClass)
|
||||||
|
{
|
||||||
|
___itemUiContext_0.RegisterCurrentItemContext(___itemContextAbstractClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TradingPanelUnregisterContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(TradingRequisitePanel), nameof(TradingRequisitePanel.method_2)); // OnHoverEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemUiContext ___itemUiContext_0, ItemContextAbstractClass ___itemContextAbstractClass)
|
||||||
|
{
|
||||||
|
___itemUiContext_0.UnregisterCurrentItemContext(___itemContextAbstractClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keybinds don't work if a textbox has focus - setting the textbox to readonly seems the best way to fix this
|
||||||
|
// without changing selection and causing weird side effects
|
||||||
|
public class SelectCurrentContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.RegisterCurrentItemContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
if (!Settings.ItemContextBlocksTextInputs.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EventSystem.current?.currentSelectedGameObject != null)
|
||||||
|
{
|
||||||
|
LastSelectedInput = EventSystem.current.currentSelectedGameObject.GetComponent<TMP_InputField>();
|
||||||
|
if (LastSelectedInput != null)
|
||||||
|
{
|
||||||
|
LastSelectedInput.readOnly = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DeselectCurrentContextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.UnregisterCurrentItemContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
if (!Settings.ItemContextBlocksTextInputs.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LastSelectedInput != null)
|
||||||
|
{
|
||||||
|
LastSelectedInput.readOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LastSelectedInput = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,94 +6,93 @@ using TMPro;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class FixFleaPatches
|
||||||
{
|
{
|
||||||
public static class FixFleaPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
// These two are anal AF
|
||||||
{
|
new DoNotToggleOnMouseOverPatch().Enable();
|
||||||
// These two are anal AF
|
new ToggleOnOpenPatch().Enable();
|
||||||
new DoNotToggleOnMouseOverPatch().Enable();
|
|
||||||
new ToggleOnOpenPatch().Enable();
|
|
||||||
|
|
||||||
new OfferItemFixMaskPatch().Enable();
|
new OfferItemFixMaskPatch().Enable();
|
||||||
new OfferViewTweaksPatch().Enable();
|
new OfferViewTweaksPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DoNotToggleOnMouseOverPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(CategoryView), nameof(CategoryView.PointerEnterHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DoNotToggleOnMouseOverPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(Image ____toggleImage, Sprite ____closeSprite, bool ___bool_3)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!___bool_3)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(CategoryView), nameof(CategoryView.PointerEnterHandler));
|
____toggleImage.sprite = ____closeSprite;
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(Image ____toggleImage, Sprite ____closeSprite, bool ___bool_3)
|
|
||||||
{
|
|
||||||
if (!___bool_3)
|
|
||||||
{
|
|
||||||
____toggleImage.sprite = ____closeSprite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ToggleOnOpenPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(CategoryView), nameof(CategoryView.OpenCategory));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(Image ____toggleImage, Sprite ____openSprite, bool ___bool_3)
|
|
||||||
{
|
|
||||||
if (___bool_3)
|
|
||||||
{
|
|
||||||
____toggleImage.sprite = ____openSprite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OfferItemFixMaskPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(OfferItemDescription), nameof(OfferItemDescription.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(TextMeshProUGUI ____offerItemName)
|
|
||||||
{
|
|
||||||
____offerItemName.maskable = true;
|
|
||||||
foreach (var item in ____offerItemName.GetComponentsInChildren<TMP_SubMeshUI>())
|
|
||||||
{
|
|
||||||
item.maskable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OfferViewTweaksPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(OfferView), nameof(OfferView.Awake));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(OfferView __instance, GameObject ____expirationTimePanel)
|
|
||||||
{
|
|
||||||
// Intercept clicks on the actions area
|
|
||||||
var blocker = __instance.transform.Find("Actions").gameObject.GetOrAddComponent<Button>();
|
|
||||||
blocker.transition = Selectable.Transition.None;
|
|
||||||
|
|
||||||
// But enable clicks specifically on the minimize button
|
|
||||||
var minimizeButton = __instance.transform.Find("Actions/MinimizeButton").gameObject.GetOrAddComponent<Button>();
|
|
||||||
minimizeButton.onClick.AddListener(() => __instance.OnPointerClick(null));
|
|
||||||
|
|
||||||
// Stop expiration clock from dancing around
|
|
||||||
var timeLeft = ____expirationTimePanel.transform.Find("TimeLeft").GetComponent<HorizontalLayoutGroup>();
|
|
||||||
timeLeft.childControlWidth = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ToggleOnOpenPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(CategoryView), nameof(CategoryView.OpenCategory));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(Image ____toggleImage, Sprite ____openSprite, bool ___bool_3)
|
||||||
|
{
|
||||||
|
if (___bool_3)
|
||||||
|
{
|
||||||
|
____toggleImage.sprite = ____openSprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OfferItemFixMaskPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(OfferItemDescription), nameof(OfferItemDescription.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(TextMeshProUGUI ____offerItemName)
|
||||||
|
{
|
||||||
|
____offerItemName.maskable = true;
|
||||||
|
foreach (var item in ____offerItemName.GetComponentsInChildren<TMP_SubMeshUI>())
|
||||||
|
{
|
||||||
|
item.maskable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OfferViewTweaksPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(OfferView), nameof(OfferView.Awake));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(OfferView __instance, GameObject ____expirationTimePanel)
|
||||||
|
{
|
||||||
|
// Intercept clicks on the actions area
|
||||||
|
var blocker = __instance.transform.Find("Actions").gameObject.GetOrAddComponent<Button>();
|
||||||
|
blocker.transition = Selectable.Transition.None;
|
||||||
|
|
||||||
|
// But enable clicks specifically on the minimize button
|
||||||
|
var minimizeButton = __instance.transform.Find("Actions/MinimizeButton").gameObject.GetOrAddComponent<Button>();
|
||||||
|
minimizeButton.onClick.AddListener(() => __instance.OnPointerClick(null));
|
||||||
|
|
||||||
|
// Stop expiration clock from dancing around
|
||||||
|
var timeLeft = ____expirationTimePanel.transform.Find("TimeLeft").GetComponent<HorizontalLayoutGroup>();
|
||||||
|
timeLeft.childControlWidth = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,20 @@ using HarmonyLib;
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
{
|
|
||||||
public class FixMailRecieveAllPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ChatMessageSendBlock), nameof(ChatMessageSendBlock.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
public class FixMailRecieveAllPatch : ModulePatch
|
||||||
public static void Prefix(DialogueClass dialogue)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
// Force this false will recalculate each time. This is less than ideal, but the way the code is structured makes it very difficult to do correctly.
|
{
|
||||||
dialogue.HasMessagesWithRewards = false;
|
return AccessTools.Method(typeof(ChatMessageSendBlock), nameof(ChatMessageSendBlock.Show));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(DialogueClass dialogue)
|
||||||
|
{
|
||||||
|
// Force this false will recalculate each time. This is less than ideal, but the way the code is structured makes it very difficult to do correctly.
|
||||||
|
dialogue.HasMessagesWithRewards = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,64 +6,63 @@ using System.Reflection;
|
|||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class FixTooltipPatches
|
||||||
{
|
{
|
||||||
public static class FixTooltipPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new QuestTooltipPatch().Enable();
|
||||||
|
new ArmorTooltipPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QuestTooltipPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new QuestTooltipPatch().Enable();
|
return AccessTools.Method(typeof(QuestItemViewPanel), nameof(QuestItemViewPanel.method_2));
|
||||||
new ArmorTooltipPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QuestTooltipPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(QuestItemViewPanel __instance)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
GridItemView parent = __instance.GetComponentInParent<GridItemView>();
|
||||||
{
|
parent?.ShowTooltip();
|
||||||
return AccessTools.Method(typeof(QuestItemViewPanel), nameof(QuestItemViewPanel.method_2));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
public class ArmorTooltipPatch : ModulePatch
|
||||||
public static void Postfix(QuestItemViewPanel __instance)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
GridItemView parent = __instance.GetComponentInParent<GridItemView>();
|
{
|
||||||
parent?.ShowTooltip();
|
return AccessTools.Method(typeof(GridItemView), nameof(GridItemView.NewGridItemView));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ArmorTooltipPatch : ModulePatch
|
// BSG loves to implement the same stuff in totally different ways, and this way is bad and also wrong
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(GridItemView __instance, TextMeshProUGUI ___ItemValue, PointerEventsProxy ____valuePointerEventsProxy, QuestItemViewPanel ____questsItemViewPanel)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
// Add hover events to the correct place
|
||||||
|
HoverTrigger trigger = ___ItemValue.GetComponent<HoverTrigger>();
|
||||||
|
if (trigger == null)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(GridItemView), nameof(GridItemView.NewGridItemView));
|
trigger = ___ItemValue.gameObject.AddComponent<HoverTrigger>();
|
||||||
}
|
trigger.OnHoverStart += eventData => __instance.method_31();
|
||||||
|
trigger.OnHoverEnd += eventData =>
|
||||||
// BSG loves to implement the same stuff in totally different ways, and this way is bad and also wrong
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(GridItemView __instance, TextMeshProUGUI ___ItemValue, PointerEventsProxy ____valuePointerEventsProxy, QuestItemViewPanel ____questsItemViewPanel)
|
|
||||||
{
|
|
||||||
// Add hover events to the correct place
|
|
||||||
HoverTrigger trigger = ___ItemValue.GetComponent<HoverTrigger>();
|
|
||||||
if (trigger == null)
|
|
||||||
{
|
{
|
||||||
trigger = ___ItemValue.gameObject.AddComponent<HoverTrigger>();
|
__instance.method_32();
|
||||||
trigger.OnHoverStart += eventData => __instance.method_31();
|
__instance.ShowTooltip();
|
||||||
trigger.OnHoverEnd += eventData =>
|
};
|
||||||
{
|
|
||||||
__instance.method_32();
|
|
||||||
__instance.ShowTooltip();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Need a child component for some reason, copying how the quest item tooltip does it
|
// Need a child component for some reason, copying how the quest item tooltip does it
|
||||||
Transform hover = ____questsItemViewPanel?.transform.Find("Hover");
|
Transform hover = ____questsItemViewPanel?.transform.Find("Hover");
|
||||||
if (hover != null)
|
if (hover != null)
|
||||||
{
|
{
|
||||||
UnityEngine.Object.Instantiate(hover, trigger.transform, false);
|
UnityEngine.Object.Instantiate(hover, trigger.transform, false);
|
||||||
}
|
|
||||||
|
|
||||||
// Remove old hover handler that covered the whole info panel
|
|
||||||
UnityEngine.Object.Destroy(____valuePointerEventsProxy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove old hover handler that covered the whole info panel
|
||||||
|
UnityEngine.Object.Destroy(____valuePointerEventsProxy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,79 +4,78 @@ using HarmonyLib;
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class FixTraderControllerSimulateFalsePatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class FixTraderControllerSimulateFalsePatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(TraderControllerClass), nameof(TraderControllerClass.ExecutePossibleAction), [typeof(ItemContextAbstractClass), typeof(Item), typeof(bool), typeof(bool)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recreating this function to add the comment section, so calling this with simulate = false doesn't break everything
|
||||||
|
[PatchPrefix]
|
||||||
|
[HarmonyPriority(Priority.Last)]
|
||||||
|
public static bool Prefix(TraderControllerClass __instance, ItemContextAbstractClass itemContext, Item targetItem, bool partialTransferOnly, bool simulate, ref ItemOperation __result)
|
||||||
|
{
|
||||||
|
TargetItemOperation opStruct;
|
||||||
|
opStruct.targetItem = targetItem;
|
||||||
|
opStruct.traderControllerClass = __instance;
|
||||||
|
opStruct.simulate = simulate;
|
||||||
|
opStruct.item = itemContext.Item;
|
||||||
|
|
||||||
|
Error error = new NoPossibleActionsError(opStruct.item);
|
||||||
|
bool mergeAvailable = itemContext.MergeAvailable;
|
||||||
|
bool splitAvailable = itemContext.SplitAvailable;
|
||||||
|
partialTransferOnly &= splitAvailable;
|
||||||
|
|
||||||
|
if (mergeAvailable)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(TraderControllerClass), nameof(TraderControllerClass.ExecutePossibleAction), [typeof(ItemContextAbstractClass), typeof(Item), typeof(bool), typeof(bool)]);
|
if (partialTransferOnly)
|
||||||
|
{
|
||||||
|
__result = __instance.method_24(ref error, ref opStruct);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var operation = __instance.method_22(ref error, ref opStruct);
|
||||||
|
if (operation.Succeeded)
|
||||||
|
{
|
||||||
|
__result = operation;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recreating this function to add the comment section, so calling this with simulate = false doesn't break everything
|
if (opStruct.targetItem is IApplicable applicable)
|
||||||
[PatchPrefix]
|
|
||||||
[HarmonyPriority(Priority.Last)]
|
|
||||||
public static bool Prefix(TraderControllerClass __instance, ItemContextAbstractClass itemContext, Item targetItem, bool partialTransferOnly, bool simulate, ref ItemOperation __result)
|
|
||||||
{
|
{
|
||||||
TargetItemOperation opStruct;
|
var operation = __instance.method_23(applicable, ref error, ref opStruct);
|
||||||
opStruct.targetItem = targetItem;
|
if (operation.Succeeded)
|
||||||
opStruct.traderControllerClass = __instance;
|
|
||||||
opStruct.simulate = simulate;
|
|
||||||
opStruct.item = itemContext.Item;
|
|
||||||
|
|
||||||
Error error = new NoPossibleActionsError(opStruct.item);
|
|
||||||
bool mergeAvailable = itemContext.MergeAvailable;
|
|
||||||
bool splitAvailable = itemContext.SplitAvailable;
|
|
||||||
partialTransferOnly &= splitAvailable;
|
|
||||||
|
|
||||||
if (mergeAvailable)
|
|
||||||
{
|
{
|
||||||
if (partialTransferOnly)
|
if (itemContext.IsOperationAllowed(operation.Value))
|
||||||
{
|
|
||||||
__result = __instance.method_24(ref error, ref opStruct);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var operation = __instance.method_22(ref error, ref opStruct);
|
|
||||||
if (operation.Succeeded)
|
|
||||||
{
|
{
|
||||||
__result = operation;
|
__result = operation;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
// Begin added section
|
||||||
|
else if (!simulate && operation.Value != null)
|
||||||
if (opStruct.targetItem is IApplicable applicable)
|
|
||||||
{
|
|
||||||
var operation = __instance.method_23(applicable, ref error, ref opStruct);
|
|
||||||
if (operation.Succeeded)
|
|
||||||
{
|
{
|
||||||
if (itemContext.IsOperationAllowed(operation.Value))
|
// BSG dropped this operation on the floor, but it needs to be rolled back if it's not going to be returned
|
||||||
{
|
operation.Value.RollBack();
|
||||||
__result = operation;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Begin added section
|
|
||||||
else if (!simulate && operation.Value != null)
|
|
||||||
{
|
|
||||||
// BSG dropped this operation on the floor, but it needs to be rolled back if it's not going to be returned
|
|
||||||
operation.Value.RollBack();
|
|
||||||
}
|
|
||||||
// End added section
|
|
||||||
}
|
}
|
||||||
|
// End added section
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mergeAvailable && splitAvailable)
|
|
||||||
{
|
|
||||||
var operation = __instance.method_24(ref error, ref opStruct);
|
|
||||||
if (operation.Succeeded)
|
|
||||||
{
|
|
||||||
__result = operation;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__result = error;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mergeAvailable && splitAvailable)
|
||||||
|
{
|
||||||
|
var operation = __instance.method_24(ref error, ref opStruct);
|
||||||
|
if (operation.Succeeded)
|
||||||
|
{
|
||||||
|
__result = operation;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__result = error;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,49 +4,48 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class FixUnloadLastBulletPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class FixUnloadLastBulletPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(TraderControllerClass), nameof(TraderControllerClass.HasForeignEvents));
|
||||||
{
|
}
|
||||||
return AccessTools.Method(typeof(TraderControllerClass), nameof(TraderControllerClass.HasForeignEvents));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reimplement because theirs is wrong
|
// Reimplement because theirs is wrong
|
||||||
[PatchPrefix]
|
[PatchPrefix]
|
||||||
public static bool Prefix(TraderControllerClass __instance, Item item, TraderControllerClass anotherOwner, ref bool __result, List<Item> ___list_2)
|
public static bool Prefix(TraderControllerClass __instance, Item item, TraderControllerClass anotherOwner, ref bool __result, List<Item> ___list_2)
|
||||||
|
{
|
||||||
|
if (__instance == anotherOwner)
|
||||||
{
|
{
|
||||||
if (__instance == anotherOwner)
|
|
||||||
{
|
|
||||||
__result = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
___list_2.Clear();
|
|
||||||
item.GetAllItemsNonAlloc(___list_2, false, true);
|
|
||||||
foreach (Item item2 in ___list_2)
|
|
||||||
{
|
|
||||||
foreach (var eventArgs in __instance.List_0)
|
|
||||||
{
|
|
||||||
ItemAddress location = eventArgs.GetLocation();
|
|
||||||
if (!eventArgs.OwnerId.Equals(anotherOwner.ID) && !eventArgs.OwnerId.Equals(__instance.ID)) // checking against this is what I changed
|
|
||||||
{
|
|
||||||
if (item2 == eventArgs.Item)
|
|
||||||
{
|
|
||||||
__result = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (location != null && location.Container.ParentItem == item2)
|
|
||||||
{
|
|
||||||
__result = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__result = false;
|
__result = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
___list_2.Clear();
|
||||||
|
item.GetAllItemsNonAlloc(___list_2, false, true);
|
||||||
|
foreach (Item item2 in ___list_2)
|
||||||
|
{
|
||||||
|
foreach (var eventArgs in __instance.List_0)
|
||||||
|
{
|
||||||
|
ItemAddress location = eventArgs.GetLocation();
|
||||||
|
if (!eventArgs.OwnerId.Equals(anotherOwner.ID) && !eventArgs.OwnerId.Equals(__instance.ID)) // checking against this is what I changed
|
||||||
|
{
|
||||||
|
if (item2 == eventArgs.Item)
|
||||||
|
{
|
||||||
|
__result = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (location != null && location.Container.ParentItem == item2)
|
||||||
|
{
|
||||||
|
__result = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__result = false;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,425 +11,424 @@ using System.Threading.Tasks;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class FleaPrevSearchPatches
|
||||||
{
|
{
|
||||||
public static class FleaPrevSearchPatches
|
private class HistoryEntry
|
||||||
{
|
{
|
||||||
private class HistoryEntry
|
public FilterRule filterRule;
|
||||||
|
public float scrollPosition = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Stack<HistoryEntry> History = new();
|
||||||
|
|
||||||
|
private static string DelayedHandbookId = string.Empty;
|
||||||
|
private static float PossibleScrollPosition = -1f;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
|
{
|
||||||
|
new RagfairScreenShowPatch().Enable();
|
||||||
|
new OfferViewListCategoryPickedPatch().Enable();
|
||||||
|
new OfferViewListDoneLoadingPatch().Enable();
|
||||||
|
new ChangedViewListTypePatch().Enable();
|
||||||
|
|
||||||
|
Settings.EnableFleaHistory.Subscribe(enabled =>
|
||||||
{
|
{
|
||||||
public FilterRule filterRule;
|
if (!enabled && PreviousFilterButton.Instance != null)
|
||||||
public float scrollPosition = 0f;
|
{
|
||||||
|
UnityEngine.Object.Destroy(PreviousFilterButton.Instance.gameObject);
|
||||||
|
PreviousFilterButton.Instance = null;
|
||||||
|
History.Clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PreviousFilterButton : MonoBehaviour
|
||||||
|
{
|
||||||
|
private RagFairClass ragfair;
|
||||||
|
private RagfairScreen ragfairScreen;
|
||||||
|
private DefaultUIButton button;
|
||||||
|
private LayoutElement layoutElement;
|
||||||
|
|
||||||
|
private bool goingBack = false;
|
||||||
|
|
||||||
|
public static PreviousFilterButton Instance;
|
||||||
|
|
||||||
|
public void Awake()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
button = GetComponent<DefaultUIButton>();
|
||||||
|
layoutElement = GetComponent<LayoutElement>();
|
||||||
|
|
||||||
|
button.OnClick.RemoveAllListeners();
|
||||||
|
button.OnClick.AddListener(OnClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Stack<HistoryEntry> History = new();
|
public void Show(RagfairScreen ragfairScreen, RagFairClass ragfair)
|
||||||
|
|
||||||
private static string DelayedHandbookId = string.Empty;
|
|
||||||
private static float PossibleScrollPosition = -1f;
|
|
||||||
|
|
||||||
public static void Enable()
|
|
||||||
{
|
{
|
||||||
new RagfairScreenShowPatch().Enable();
|
this.ragfair = ragfair;
|
||||||
new OfferViewListCategoryPickedPatch().Enable();
|
this.ragfairScreen = ragfairScreen;
|
||||||
new OfferViewListDoneLoadingPatch().Enable();
|
|
||||||
new ChangedViewListTypePatch().Enable();
|
|
||||||
|
|
||||||
Settings.EnableFleaHistory.Subscribe(enabled =>
|
button.SetRawText("< " + "back".Localized(), 20);
|
||||||
|
|
||||||
|
layoutElement.minWidth = -1;
|
||||||
|
layoutElement.preferredWidth = -1;
|
||||||
|
|
||||||
|
// Prime the first filter
|
||||||
|
if (!History.Any())
|
||||||
{
|
{
|
||||||
if (!enabled && PreviousFilterButton.Instance != null)
|
History.Push(new HistoryEntry() { filterRule = ragfair.method_3(EViewListType.AllOffers) }); // Player's saved default rule
|
||||||
{
|
}
|
||||||
UnityEngine.Object.Destroy(PreviousFilterButton.Instance.gameObject);
|
|
||||||
PreviousFilterButton.Instance = null;
|
// Load what they're searching now, which may or may not be the same as the default
|
||||||
History.Clear();
|
OnFilterRuleChanged();
|
||||||
}
|
|
||||||
});
|
ragfair.OnFilterRuleChanged += OnFilterRuleChanged;
|
||||||
|
|
||||||
|
if (History.Count < 2)
|
||||||
|
{
|
||||||
|
button.Interactable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameObject.SetActive(ragfair.FilterRule.ViewListType == EViewListType.AllOffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PreviousFilterButton : MonoBehaviour
|
public void Close()
|
||||||
{
|
{
|
||||||
private RagFairClass ragfair;
|
ragfair.OnFilterRuleChanged -= OnFilterRuleChanged;
|
||||||
private RagfairScreen ragfairScreen;
|
ragfair = null;
|
||||||
private DefaultUIButton button;
|
ragfairScreen = null;
|
||||||
private LayoutElement layoutElement;
|
}
|
||||||
|
|
||||||
private bool goingBack = false;
|
public void OnOffersLoaded(OfferViewList offerViewList)
|
||||||
|
{
|
||||||
public static PreviousFilterButton Instance;
|
if (!String.IsNullOrEmpty(DelayedHandbookId))
|
||||||
|
|
||||||
public void Awake()
|
|
||||||
{
|
{
|
||||||
Instance = this;
|
// Super important to clear DelayedHandbookId *before* calling method_10, or infinite loops can occur!
|
||||||
button = GetComponent<DefaultUIButton>();
|
string newHandbookId = DelayedHandbookId;
|
||||||
layoutElement = GetComponent<LayoutElement>();
|
DelayedHandbookId = string.Empty;
|
||||||
|
|
||||||
button.OnClick.RemoveAllListeners();
|
offerViewList.method_10(newHandbookId, false);
|
||||||
button.OnClick.AddListener(OnClick);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Show(RagfairScreen ragfairScreen, RagFairClass ragfair)
|
// Restore scroll position now that offers are loaded
|
||||||
|
if (History.Any())
|
||||||
{
|
{
|
||||||
this.ragfair = ragfair;
|
offerViewList.R().Scroller.SetScrollPosition(History.Peek().scrollPosition);
|
||||||
this.ragfairScreen = ragfairScreen;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
button.SetRawText("< " + "back".Localized(), 20);
|
private void OnClick()
|
||||||
|
{
|
||||||
layoutElement.minWidth = -1;
|
History.Pop(); // remove current
|
||||||
layoutElement.preferredWidth = -1;
|
if (History.Count < 2)
|
||||||
|
{
|
||||||
// Prime the first filter
|
button.Interactable = false;
|
||||||
if (!History.Any())
|
|
||||||
{
|
|
||||||
History.Push(new HistoryEntry() { filterRule = ragfair.method_3(EViewListType.AllOffers) }); // Player's saved default rule
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load what they're searching now, which may or may not be the same as the default
|
|
||||||
OnFilterRuleChanged();
|
|
||||||
|
|
||||||
ragfair.OnFilterRuleChanged += OnFilterRuleChanged;
|
|
||||||
|
|
||||||
if (History.Count < 2)
|
|
||||||
{
|
|
||||||
button.Interactable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gameObject.SetActive(ragfair.FilterRule.ViewListType == EViewListType.AllOffers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close()
|
HistoryEntry previousEntry = History.Peek();
|
||||||
|
|
||||||
|
// Manually update parts of the UI because BSG sucks
|
||||||
|
UpdateColumnHeaders(ragfairScreen.R().OfferViewList.R().FiltersPanel, previousEntry.filterRule.SortType, previousEntry.filterRule.SortDirection);
|
||||||
|
|
||||||
|
goingBack = true;
|
||||||
|
ApplyFullFilter(previousEntry.filterRule);
|
||||||
|
goingBack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFilterRuleChanged(RagFairClass.ESetFilterSource source = 0, bool clear = false, bool updateCategories = false)
|
||||||
|
{
|
||||||
|
if (goingBack || !string.IsNullOrEmpty(DelayedHandbookId) || ragfair.FilterRule.ViewListType != EViewListType.AllOffers)
|
||||||
{
|
{
|
||||||
ragfair.OnFilterRuleChanged -= OnFilterRuleChanged;
|
return;
|
||||||
ragfair = null;
|
|
||||||
ragfairScreen = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnOffersLoaded(OfferViewList offerViewList)
|
HistoryEntry current = History.Any() ? History.Peek() : null;
|
||||||
|
if (current != null && current.filterRule.IsSimilarTo(ragfair.FilterRule))
|
||||||
{
|
{
|
||||||
if (!String.IsNullOrEmpty(DelayedHandbookId))
|
// Minor filter change, just update the current one
|
||||||
{
|
current.filterRule = ragfair.FilterRule;
|
||||||
// Super important to clear DelayedHandbookId *before* calling method_10, or infinite loops can occur!
|
return;
|
||||||
string newHandbookId = DelayedHandbookId;
|
|
||||||
DelayedHandbookId = string.Empty;
|
|
||||||
|
|
||||||
offerViewList.method_10(newHandbookId, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore scroll position now that offers are loaded
|
|
||||||
if (History.Any())
|
|
||||||
{
|
|
||||||
offerViewList.R().Scroller.SetScrollPosition(History.Peek().scrollPosition);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClick()
|
// Save the current scroll position before pushing the new entry
|
||||||
|
if (current != null)
|
||||||
{
|
{
|
||||||
History.Pop(); // remove current
|
if (PossibleScrollPosition >= 0f)
|
||||||
if (History.Count < 2)
|
|
||||||
{
|
{
|
||||||
button.Interactable = false;
|
current.scrollPosition = PossibleScrollPosition;
|
||||||
}
|
|
||||||
|
|
||||||
HistoryEntry previousEntry = History.Peek();
|
|
||||||
|
|
||||||
// Manually update parts of the UI because BSG sucks
|
|
||||||
UpdateColumnHeaders(ragfairScreen.R().OfferViewList.R().FiltersPanel, previousEntry.filterRule.SortType, previousEntry.filterRule.SortDirection);
|
|
||||||
|
|
||||||
goingBack = true;
|
|
||||||
ApplyFullFilter(previousEntry.filterRule);
|
|
||||||
goingBack = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnFilterRuleChanged(RagFairClass.ESetFilterSource source = 0, bool clear = false, bool updateCategories = false)
|
|
||||||
{
|
|
||||||
if (goingBack || !string.IsNullOrEmpty(DelayedHandbookId) || ragfair.FilterRule.ViewListType != EViewListType.AllOffers)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HistoryEntry current = History.Any() ? History.Peek() : null;
|
|
||||||
if (current != null && current.filterRule.IsSimilarTo(ragfair.FilterRule))
|
|
||||||
{
|
|
||||||
// Minor filter change, just update the current one
|
|
||||||
current.filterRule = ragfair.FilterRule;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the current scroll position before pushing the new entry
|
|
||||||
if (current != null)
|
|
||||||
{
|
|
||||||
if (PossibleScrollPosition >= 0f)
|
|
||||||
{
|
|
||||||
current.scrollPosition = PossibleScrollPosition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LightScroller scroller = ragfairScreen.R().OfferViewList.R().Scroller;
|
|
||||||
current.scrollPosition = scroller.NormalizedScrollPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
History.Push(new HistoryEntry() { filterRule = ragfair.FilterRule });
|
|
||||||
|
|
||||||
if (History.Count >= 2)
|
|
||||||
{
|
|
||||||
button.Interactable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic sanity to keep this from growing out of control
|
|
||||||
if (History.Count > 50)
|
|
||||||
{
|
|
||||||
var tempStack = new Stack<HistoryEntry>();
|
|
||||||
for (int i = History.Count / 2; i >= 0; i--)
|
|
||||||
{
|
|
||||||
tempStack.Push(History.Pop());
|
|
||||||
}
|
|
||||||
|
|
||||||
History.Clear();
|
|
||||||
|
|
||||||
while (tempStack.Any())
|
|
||||||
{
|
|
||||||
History.Push(tempStack.Pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from RagFairClass.AddSearchesInRule, but actually all of the properties
|
|
||||||
private void ApplyFullFilter(FilterRule filterRule)
|
|
||||||
{
|
|
||||||
// Order impacts the order the filters show in the UI
|
|
||||||
var searches = new List<RagfairSearch>();
|
|
||||||
|
|
||||||
// This part was tricky to figure out. Adding OR removing any of these ID filters will clear the others, so you can only do one of them.
|
|
||||||
// When going to a state with no id filter, you MUST remove something (or all to be safe)
|
|
||||||
if (!filterRule.FilterSearchId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
searches.Add(new(EFilterType.FilterSearch, filterRule.FilterSearchId, true));
|
|
||||||
}
|
|
||||||
else if (!filterRule.NeededSearchId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
searches.Add(new(EFilterType.NeededSearch, filterRule.NeededSearchId, true));
|
|
||||||
}
|
|
||||||
else if (!filterRule.LinkedSearchId.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
searches.Add(new(EFilterType.LinkedSearch, filterRule.LinkedSearchId, true));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
searches.Add(new(EFilterType.FilterSearch, String.Empty, false));
|
LightScroller scroller = ragfairScreen.R().OfferViewList.R().Scroller;
|
||||||
searches.Add(new(EFilterType.NeededSearch, String.Empty, false));
|
current.scrollPosition = scroller.NormalizedScrollPosition;
|
||||||
searches.Add(new(EFilterType.LinkedSearch, String.Empty, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
searches.Add(new(EFilterType.Currency, filterRule.CurrencyType, filterRule.CurrencyType != 0));
|
|
||||||
searches.Add(new(EFilterType.PriceFrom, filterRule.PriceFrom, filterRule.PriceFrom != 0));
|
|
||||||
searches.Add(new(EFilterType.PriceTo, filterRule.PriceTo, filterRule.PriceTo != 0));
|
|
||||||
searches.Add(new(EFilterType.QuantityFrom, filterRule.QuantityFrom, filterRule.QuantityFrom != 0));
|
|
||||||
searches.Add(new(EFilterType.QuantityTo, filterRule.QuantityTo, filterRule.QuantityTo != 0));
|
|
||||||
searches.Add(new(EFilterType.ConditionFrom, filterRule.ConditionFrom, filterRule.ConditionFrom != 0));
|
|
||||||
searches.Add(new(EFilterType.ConditionTo, filterRule.ConditionTo, filterRule.ConditionTo != 100));
|
|
||||||
searches.Add(new(EFilterType.OneHourExpiration, filterRule.OneHourExpiration ? 1 : 0, filterRule.OneHourExpiration));
|
|
||||||
searches.Add(new(EFilterType.RemoveBartering, filterRule.RemoveBartering ? 1 : 0, filterRule.RemoveBartering));
|
|
||||||
searches.Add(new(EFilterType.OfferOwnerType, filterRule.OfferOwnerType, filterRule.OfferOwnerType != 0));
|
|
||||||
searches.Add(new(EFilterType.OnlyFunctional, filterRule.OnlyFunctional ? 1 : 0, filterRule.OnlyFunctional));
|
|
||||||
|
|
||||||
ragfair.method_24(filterRule.ViewListType, [.. searches], false, out FilterRule newRule);
|
|
||||||
|
|
||||||
// These properties don't consistute a new search, so much as a different view of the same search
|
|
||||||
newRule.Page = filterRule.Page;
|
|
||||||
newRule.SortType = filterRule.SortType;
|
|
||||||
newRule.SortDirection = filterRule.SortDirection;
|
|
||||||
|
|
||||||
// Can't set handbookId yet - it limits the result set and that in turn limits what categories even display
|
|
||||||
DelayedHandbookId = filterRule.HandbookId;
|
|
||||||
|
|
||||||
ragfair.SetFilterRule(newRule, true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateColumnHeaders(FiltersPanel filtersPanel, ESortType sortType, bool sortDirection)
|
|
||||||
{
|
|
||||||
var wrappedFiltersPanel = filtersPanel.R();
|
|
||||||
RagfairFilterButton button = sortType switch
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RagfairScreenShowPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(RagfairScreen), nameof(RagfairScreen.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(DefaultUIButton ____addOfferButton, ref PreviousFilterButton __state)
|
|
||||||
{
|
|
||||||
// Create previous button
|
|
||||||
if (!Settings.EnableFleaHistory.Value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
__state = ____addOfferButton.transform.parent.Find("PreviousFilterButton")?.GetComponent<PreviousFilterButton>();
|
|
||||||
if (__state == null)
|
|
||||||
{
|
|
||||||
var clone = UnityEngine.Object.Instantiate(____addOfferButton, ____addOfferButton.transform.parent, false);
|
|
||||||
clone.name = "PreviousFilterButton";
|
|
||||||
clone.transform.SetAsFirstSibling();
|
|
||||||
|
|
||||||
__state = clone.GetOrAddComponent<PreviousFilterButton>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
History.Push(new HistoryEntry() { filterRule = ragfair.FilterRule });
|
||||||
public static void Postfix(RagfairScreen __instance, ISession session, DefaultUIButton ____addOfferButton, PreviousFilterButton __state)
|
|
||||||
{
|
|
||||||
// Delete the upper right display options, since they aren't even implemented
|
|
||||||
var tabs = __instance.transform.Find("TopRightPanel/Tabs");
|
|
||||||
tabs?.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
if (!Settings.EnableFleaHistory.Value)
|
if (History.Count >= 2)
|
||||||
|
{
|
||||||
|
button.Interactable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic sanity to keep this from growing out of control
|
||||||
|
if (History.Count > 50)
|
||||||
|
{
|
||||||
|
var tempStack = new Stack<HistoryEntry>();
|
||||||
|
for (int i = History.Count / 2; i >= 0; i--)
|
||||||
{
|
{
|
||||||
return;
|
tempStack.Push(History.Pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
__state.Show(__instance, session.RagFair);
|
History.Clear();
|
||||||
__instance.R().UI.AddDisposable(__state.Close);
|
|
||||||
|
|
||||||
// Resize the Add Offer button to use less extra space
|
while (tempStack.Any())
|
||||||
var addOfferLayout = ____addOfferButton.GetComponent<LayoutElement>();
|
|
||||||
addOfferLayout.minWidth = -1;
|
|
||||||
addOfferLayout.preferredWidth = -1;
|
|
||||||
|
|
||||||
// Recenter the add offer text
|
|
||||||
var addOfferLabel = ____addOfferButton.transform.Find("SizeLabel");
|
|
||||||
addOfferLabel.localPosition = new Vector3(0f, 0f, 0f);
|
|
||||||
|
|
||||||
// Tighten up the spacing
|
|
||||||
var layoutGroup = PreviousFilterButton.Instance.transform.parent.GetComponent<HorizontalLayoutGroup>();
|
|
||||||
layoutGroup.spacing = 5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChangedViewListTypePatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(RagfairScreen), nameof(RagfairScreen.method_9));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(EViewListType type)
|
|
||||||
{
|
|
||||||
PreviousFilterButton.Instance?.gameObject.SetActive(type == EViewListType.AllOffers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OfferViewListCategoryPickedPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.method_10));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first thing this method does is set scrollposition to 0, so grab it first
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(LightScroller ____scroller)
|
|
||||||
{
|
|
||||||
PossibleScrollPosition = ____scroller.NormalizedScrollPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
PossibleScrollPosition = -1f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OfferViewListDoneLoadingPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.method_12));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static async void Postfix(OfferViewList __instance, Task __result, EViewListType ___eviewListType_0)
|
|
||||||
{
|
|
||||||
await __result;
|
|
||||||
|
|
||||||
if (___eviewListType_0 != EViewListType.AllOffers)
|
|
||||||
{
|
{
|
||||||
return;
|
History.Push(tempStack.Pop());
|
||||||
}
|
|
||||||
|
|
||||||
PreviousFilterButton.Instance.OnOffersLoaded(__instance);
|
|
||||||
|
|
||||||
if (Settings.AutoExpandCategories.Value)
|
|
||||||
{
|
|
||||||
// Try to auto-expand categories to use available space. Gotta do math to see what fits
|
|
||||||
const int PanelHeight = 780;
|
|
||||||
const int CategoryHeight = 34;
|
|
||||||
const int SubcategoryHeight = 25;
|
|
||||||
|
|
||||||
var activeCategories = __instance.GetComponentsInChildren<CategoryView>();
|
|
||||||
var activeSubcategories = __instance.GetComponentsInChildren<SubcategoryView>();
|
|
||||||
int currentHeight = activeCategories.Length * CategoryHeight + activeSubcategories.Length * SubcategoryHeight;
|
|
||||||
|
|
||||||
var categories = __instance.GetComponentsInChildren<CombinedView>()
|
|
||||||
.Where(cv => cv.transform.childCount > 0)
|
|
||||||
.Select(cv => cv.transform.GetChild(0).GetComponent<CategoryView>())
|
|
||||||
.Where(c => c != null && c.gameObject.activeInHierarchy);
|
|
||||||
|
|
||||||
while (categories.Any())
|
|
||||||
{
|
|
||||||
// This is all child categories that aren't already open; have matching *offers* (x.Count); and if they have children themselves they're a category, otherwise a subcategory
|
|
||||||
int additionalHeight = categories
|
|
||||||
.Where(c => !c.R().IsOpen && c.Node != null)
|
|
||||||
.SelectMany(c => c.Node.Children)
|
|
||||||
.Where(n => n.Count > 0)
|
|
||||||
.Sum(n => n.Children.Any() ? CategoryHeight : SubcategoryHeight);
|
|
||||||
|
|
||||||
if (currentHeight + additionalHeight > PanelHeight)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentHeight += additionalHeight;
|
|
||||||
categories = categories.SelectMany(c => c.OpenCategory()).Where(v => v.gameObject.activeInHierarchy).OfType<CategoryView>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commented out properties just affect the view, so consider the two filters to be a single history entry
|
// Copied from RagFairClass.AddSearchesInRule, but actually all of the properties
|
||||||
public static bool IsSimilarTo(this FilterRule one, FilterRule two)
|
private void ApplyFullFilter(FilterRule filterRule)
|
||||||
{
|
{
|
||||||
return one.ViewListType == two.ViewListType &&
|
// Order impacts the order the filters show in the UI
|
||||||
// one.Page == two.Page &&
|
var searches = new List<RagfairSearch>();
|
||||||
// one.SortType == two.SortType &&
|
|
||||||
// one.SortDirection == two.SortDirection &&
|
// This part was tricky to figure out. Adding OR removing any of these ID filters will clear the others, so you can only do one of them.
|
||||||
// one.HandbookId == two.HandbookId &&
|
// When going to a state with no id filter, you MUST remove something (or all to be safe)
|
||||||
one.CurrencyType == two.CurrencyType &&
|
if (!filterRule.FilterSearchId.IsNullOrEmpty())
|
||||||
one.PriceFrom == two.PriceFrom &&
|
{
|
||||||
one.PriceTo == two.PriceTo &&
|
searches.Add(new(EFilterType.FilterSearch, filterRule.FilterSearchId, true));
|
||||||
one.QuantityFrom == two.QuantityFrom &&
|
}
|
||||||
one.QuantityTo == two.QuantityTo &&
|
else if (!filterRule.NeededSearchId.IsNullOrEmpty())
|
||||||
one.ConditionFrom == two.ConditionFrom &&
|
{
|
||||||
one.ConditionTo == two.ConditionTo &&
|
searches.Add(new(EFilterType.NeededSearch, filterRule.NeededSearchId, true));
|
||||||
one.OneHourExpiration == two.OneHourExpiration &&
|
}
|
||||||
one.RemoveBartering == two.RemoveBartering &&
|
else if (!filterRule.LinkedSearchId.IsNullOrEmpty())
|
||||||
one.OfferOwnerType == two.OfferOwnerType &&
|
{
|
||||||
one.OnlyFunctional == two.OnlyFunctional &&
|
searches.Add(new(EFilterType.LinkedSearch, filterRule.LinkedSearchId, true));
|
||||||
one.FilterSearchId == two.FilterSearchId &&
|
}
|
||||||
one.LinkedSearchId == two.LinkedSearchId &&
|
else
|
||||||
one.NeededSearchId == two.NeededSearchId;
|
{
|
||||||
|
searches.Add(new(EFilterType.FilterSearch, String.Empty, false));
|
||||||
|
searches.Add(new(EFilterType.NeededSearch, String.Empty, false));
|
||||||
|
searches.Add(new(EFilterType.LinkedSearch, String.Empty, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
searches.Add(new(EFilterType.Currency, filterRule.CurrencyType, filterRule.CurrencyType != 0));
|
||||||
|
searches.Add(new(EFilterType.PriceFrom, filterRule.PriceFrom, filterRule.PriceFrom != 0));
|
||||||
|
searches.Add(new(EFilterType.PriceTo, filterRule.PriceTo, filterRule.PriceTo != 0));
|
||||||
|
searches.Add(new(EFilterType.QuantityFrom, filterRule.QuantityFrom, filterRule.QuantityFrom != 0));
|
||||||
|
searches.Add(new(EFilterType.QuantityTo, filterRule.QuantityTo, filterRule.QuantityTo != 0));
|
||||||
|
searches.Add(new(EFilterType.ConditionFrom, filterRule.ConditionFrom, filterRule.ConditionFrom != 0));
|
||||||
|
searches.Add(new(EFilterType.ConditionTo, filterRule.ConditionTo, filterRule.ConditionTo != 100));
|
||||||
|
searches.Add(new(EFilterType.OneHourExpiration, filterRule.OneHourExpiration ? 1 : 0, filterRule.OneHourExpiration));
|
||||||
|
searches.Add(new(EFilterType.RemoveBartering, filterRule.RemoveBartering ? 1 : 0, filterRule.RemoveBartering));
|
||||||
|
searches.Add(new(EFilterType.OfferOwnerType, filterRule.OfferOwnerType, filterRule.OfferOwnerType != 0));
|
||||||
|
searches.Add(new(EFilterType.OnlyFunctional, filterRule.OnlyFunctional ? 1 : 0, filterRule.OnlyFunctional));
|
||||||
|
|
||||||
|
ragfair.method_24(filterRule.ViewListType, [.. searches], false, out FilterRule newRule);
|
||||||
|
|
||||||
|
// These properties don't consistute a new search, so much as a different view of the same search
|
||||||
|
newRule.Page = filterRule.Page;
|
||||||
|
newRule.SortType = filterRule.SortType;
|
||||||
|
newRule.SortDirection = filterRule.SortDirection;
|
||||||
|
|
||||||
|
// Can't set handbookId yet - it limits the result set and that in turn limits what categories even display
|
||||||
|
DelayedHandbookId = filterRule.HandbookId;
|
||||||
|
|
||||||
|
ragfair.SetFilterRule(newRule, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateColumnHeaders(FiltersPanel filtersPanel, ESortType sortType, bool sortDirection)
|
||||||
|
{
|
||||||
|
var wrappedFiltersPanel = filtersPanel.R();
|
||||||
|
RagfairFilterButton button = sortType switch
|
||||||
|
{
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RagfairScreenShowPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(RagfairScreen), nameof(RagfairScreen.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(DefaultUIButton ____addOfferButton, ref PreviousFilterButton __state)
|
||||||
|
{
|
||||||
|
// Create previous button
|
||||||
|
if (!Settings.EnableFleaHistory.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__state = ____addOfferButton.transform.parent.Find("PreviousFilterButton")?.GetComponent<PreviousFilterButton>();
|
||||||
|
if (__state == null)
|
||||||
|
{
|
||||||
|
var clone = UnityEngine.Object.Instantiate(____addOfferButton, ____addOfferButton.transform.parent, false);
|
||||||
|
clone.name = "PreviousFilterButton";
|
||||||
|
clone.transform.SetAsFirstSibling();
|
||||||
|
|
||||||
|
__state = clone.GetOrAddComponent<PreviousFilterButton>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(RagfairScreen __instance, ISession session, DefaultUIButton ____addOfferButton, PreviousFilterButton __state)
|
||||||
|
{
|
||||||
|
// Delete the upper right display options, since they aren't even implemented
|
||||||
|
var tabs = __instance.transform.Find("TopRightPanel/Tabs");
|
||||||
|
tabs?.gameObject.SetActive(false);
|
||||||
|
|
||||||
|
if (!Settings.EnableFleaHistory.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__state.Show(__instance, session.RagFair);
|
||||||
|
__instance.R().UI.AddDisposable(__state.Close);
|
||||||
|
|
||||||
|
// Resize the Add Offer button to use less extra space
|
||||||
|
var addOfferLayout = ____addOfferButton.GetComponent<LayoutElement>();
|
||||||
|
addOfferLayout.minWidth = -1;
|
||||||
|
addOfferLayout.preferredWidth = -1;
|
||||||
|
|
||||||
|
// Recenter the add offer text
|
||||||
|
var addOfferLabel = ____addOfferButton.transform.Find("SizeLabel");
|
||||||
|
addOfferLabel.localPosition = new Vector3(0f, 0f, 0f);
|
||||||
|
|
||||||
|
// Tighten up the spacing
|
||||||
|
var layoutGroup = PreviousFilterButton.Instance.transform.parent.GetComponent<HorizontalLayoutGroup>();
|
||||||
|
layoutGroup.spacing = 5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChangedViewListTypePatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(RagfairScreen), nameof(RagfairScreen.method_9));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(EViewListType type)
|
||||||
|
{
|
||||||
|
PreviousFilterButton.Instance?.gameObject.SetActive(type == EViewListType.AllOffers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OfferViewListCategoryPickedPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.method_10));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first thing this method does is set scrollposition to 0, so grab it first
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(LightScroller ____scroller)
|
||||||
|
{
|
||||||
|
PossibleScrollPosition = ____scroller.NormalizedScrollPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
PossibleScrollPosition = -1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OfferViewListDoneLoadingPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.method_12));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static async void Postfix(OfferViewList __instance, Task __result, EViewListType ___eviewListType_0)
|
||||||
|
{
|
||||||
|
await __result;
|
||||||
|
|
||||||
|
if (___eviewListType_0 != EViewListType.AllOffers)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviousFilterButton.Instance.OnOffersLoaded(__instance);
|
||||||
|
|
||||||
|
if (Settings.AutoExpandCategories.Value)
|
||||||
|
{
|
||||||
|
// Try to auto-expand categories to use available space. Gotta do math to see what fits
|
||||||
|
const int PanelHeight = 780;
|
||||||
|
const int CategoryHeight = 34;
|
||||||
|
const int SubcategoryHeight = 25;
|
||||||
|
|
||||||
|
var activeCategories = __instance.GetComponentsInChildren<CategoryView>();
|
||||||
|
var activeSubcategories = __instance.GetComponentsInChildren<SubcategoryView>();
|
||||||
|
int currentHeight = activeCategories.Length * CategoryHeight + activeSubcategories.Length * SubcategoryHeight;
|
||||||
|
|
||||||
|
var categories = __instance.GetComponentsInChildren<CombinedView>()
|
||||||
|
.Where(cv => cv.transform.childCount > 0)
|
||||||
|
.Select(cv => cv.transform.GetChild(0).GetComponent<CategoryView>())
|
||||||
|
.Where(c => c != null && c.gameObject.activeInHierarchy);
|
||||||
|
|
||||||
|
while (categories.Any())
|
||||||
|
{
|
||||||
|
// This is all child categories that aren't already open; have matching *offers* (x.Count); and if they have children themselves they're a category, otherwise a subcategory
|
||||||
|
int additionalHeight = categories
|
||||||
|
.Where(c => !c.R().IsOpen && c.Node != null)
|
||||||
|
.SelectMany(c => c.Node.Children)
|
||||||
|
.Where(n => n.Count > 0)
|
||||||
|
.Sum(n => n.Children.Any() ? CategoryHeight : SubcategoryHeight);
|
||||||
|
|
||||||
|
if (currentHeight + additionalHeight > PanelHeight)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentHeight += additionalHeight;
|
||||||
|
categories = categories.SelectMany(c => c.OpenCategory()).Where(v => v.gameObject.activeInHierarchy).OfType<CategoryView>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commented out properties just affect the view, so consider the two filters to be a single history entry
|
||||||
|
public static bool IsSimilarTo(this FilterRule one, FilterRule two)
|
||||||
|
{
|
||||||
|
return one.ViewListType == two.ViewListType &&
|
||||||
|
// one.Page == two.Page &&
|
||||||
|
// one.SortType == two.SortType &&
|
||||||
|
// one.SortDirection == two.SortDirection &&
|
||||||
|
// one.HandbookId == two.HandbookId &&
|
||||||
|
one.CurrencyType == two.CurrencyType &&
|
||||||
|
one.PriceFrom == two.PriceFrom &&
|
||||||
|
one.PriceTo == two.PriceTo &&
|
||||||
|
one.QuantityFrom == two.QuantityFrom &&
|
||||||
|
one.QuantityTo == two.QuantityTo &&
|
||||||
|
one.ConditionFrom == two.ConditionFrom &&
|
||||||
|
one.ConditionTo == two.ConditionTo &&
|
||||||
|
one.OneHourExpiration == two.OneHourExpiration &&
|
||||||
|
one.RemoveBartering == two.RemoveBartering &&
|
||||||
|
one.OfferOwnerType == two.OfferOwnerType &&
|
||||||
|
one.OnlyFunctional == two.OnlyFunctional &&
|
||||||
|
one.FilterSearchId == two.FilterSearchId &&
|
||||||
|
one.LinkedSearchId == two.LinkedSearchId &&
|
||||||
|
one.NeededSearchId == two.NeededSearchId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,51 +4,50 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class FleaSlotSearchPatches
|
||||||
{
|
{
|
||||||
public static class FleaSlotSearchPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new HandbookWorkaroundPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HandbookWorkaroundPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new HandbookWorkaroundPatch().Enable();
|
return AccessTools.Method(typeof(RagFairClass), nameof(RagFairClass.method_24));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HandbookWorkaroundPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix(RagfairSearch[] searches, ref string __state, HandbookClass ___handbookClass)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.EnableSlotSearch.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(RagFairClass), nameof(RagFairClass.method_24));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
var search = searches.FirstOrDefault(s => s.Type == EFilterType.LinkedSearch && s.StringValue.Contains(":"));
|
||||||
public static void Prefix(RagfairSearch[] searches, ref string __state, HandbookClass ___handbookClass)
|
if (search != null)
|
||||||
{
|
{
|
||||||
if (!Settings.EnableSlotSearch.Value)
|
__state = search.StringValue.Split(':')[0];
|
||||||
{
|
___handbookClass[__state].Data.Id = search.StringValue;
|
||||||
return;
|
searches[searches.IndexOf(search)] = new(EFilterType.LinkedSearch, __state, search.Add);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var search = searches.FirstOrDefault(s => s.Type == EFilterType.LinkedSearch && s.StringValue.Contains(":"));
|
[PatchPostfix]
|
||||||
if (search != null)
|
public static void Postfix(ref string __state, HandbookClass ___handbookClass)
|
||||||
{
|
{
|
||||||
__state = search.StringValue.Split(':')[0];
|
if (!Settings.EnableSlotSearch.Value)
|
||||||
___handbookClass[__state].Data.Id = search.StringValue;
|
{
|
||||||
searches[searches.IndexOf(search)] = new(EFilterType.LinkedSearch, __state, search.Add);
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (__state != null)
|
||||||
public static void Postfix(ref string __state, HandbookClass ___handbookClass)
|
|
||||||
{
|
{
|
||||||
if (!Settings.EnableSlotSearch.Value)
|
___handbookClass[__state].Data.Id = __state;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__state != null)
|
|
||||||
{
|
|
||||||
___handbookClass[__state].Data.Id = __state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,69 +6,68 @@ using System.Reflection;
|
|||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class FocusFleaOfferNumberPatches
|
||||||
{
|
{
|
||||||
public static class FocusFleaOfferNumberPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new MoneyPatch().Enable();
|
||||||
|
new BarterPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MoneyPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new MoneyPatch().Enable();
|
return AccessTools.DeclaredMethod(typeof(HandoverRagfairMoneyWindow), nameof(HandoverRagfairMoneyWindow.Show));
|
||||||
new BarterPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MoneyPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(HandoverRagfairMoneyWindow __instance, TMP_InputField ____inputField)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
AllButtonKeybind allKeybind = __instance.GetOrAddComponent<AllButtonKeybind>();
|
||||||
{
|
allKeybind.Init(__instance.method_9);
|
||||||
return AccessTools.DeclaredMethod(typeof(HandoverRagfairMoneyWindow), nameof(HandoverRagfairMoneyWindow.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
____inputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
||||||
public static void Postfix(HandoverRagfairMoneyWindow __instance, TMP_InputField ____inputField)
|
____inputField.ActivateInputField();
|
||||||
{
|
____inputField.Select();
|
||||||
AllButtonKeybind allKeybind = __instance.GetOrAddComponent<AllButtonKeybind>();
|
}
|
||||||
allKeybind.Init(__instance.method_9);
|
}
|
||||||
|
|
||||||
____inputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
public class BarterPatch : ModulePatch
|
||||||
____inputField.ActivateInputField();
|
{
|
||||||
____inputField.Select();
|
protected override MethodBase GetTargetMethod()
|
||||||
}
|
{
|
||||||
|
return AccessTools.DeclaredMethod(typeof(HandoverExchangeableItemsWindow), nameof(HandoverExchangeableItemsWindow.Show));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BarterPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(HandoverExchangeableItemsWindow __instance, TMP_InputField ____inputField)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
AllButtonKeybind allKeybind = __instance.GetOrAddComponent<AllButtonKeybind>();
|
||||||
{
|
allKeybind.Init(__instance.method_16);
|
||||||
return AccessTools.DeclaredMethod(typeof(HandoverExchangeableItemsWindow), nameof(HandoverExchangeableItemsWindow.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
____inputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
||||||
public static void Postfix(HandoverExchangeableItemsWindow __instance, TMP_InputField ____inputField)
|
____inputField.ActivateInputField();
|
||||||
{
|
____inputField.Select();
|
||||||
AllButtonKeybind allKeybind = __instance.GetOrAddComponent<AllButtonKeybind>();
|
}
|
||||||
allKeybind.Init(__instance.method_16);
|
}
|
||||||
|
|
||||||
____inputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
public class AllButtonKeybind : MonoBehaviour
|
||||||
____inputField.ActivateInputField();
|
{
|
||||||
____inputField.Select();
|
private Action purchaseAllAction;
|
||||||
}
|
|
||||||
|
public void Init(Action purchaseAllAction)
|
||||||
|
{
|
||||||
|
this.purchaseAllAction = purchaseAllAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AllButtonKeybind : MonoBehaviour
|
public void Update()
|
||||||
{
|
{
|
||||||
private Action purchaseAllAction;
|
if (Settings.PurchaseAllKeybind.Value.IsDown())
|
||||||
|
|
||||||
public void Init(Action purchaseAllAction)
|
|
||||||
{
|
{
|
||||||
this.purchaseAllAction = purchaseAllAction;
|
purchaseAllAction();
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (Settings.PurchaseAllKeybind.Value.IsDown())
|
|
||||||
{
|
|
||||||
purchaseAllAction();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,23 +4,22 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
{
|
|
||||||
public class FocusTradeQuantityPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BarterSchemePanel), nameof(BarterSchemePanel.method_10));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets called on the TransactionChanged event.
|
public class FocusTradeQuantityPatch : ModulePatch
|
||||||
// The reason quantity isn't focused on the 2nd+ purchase is that BSG calls ActivateInputField() and Select() before the transaction is finished
|
{
|
||||||
// During the transaction, the whole canvas group is not interactable, and these methods don't work on non-interactable fields
|
protected override MethodBase GetTargetMethod()
|
||||||
[PatchPostfix]
|
{
|
||||||
public static void Postfix(TMP_InputField ____quantity)
|
return AccessTools.Method(typeof(BarterSchemePanel), nameof(BarterSchemePanel.method_10));
|
||||||
{
|
}
|
||||||
____quantity.ActivateInputField();
|
|
||||||
____quantity.Select();
|
// Gets called on the TransactionChanged event.
|
||||||
}
|
// The reason quantity isn't focused on the 2nd+ purchase is that BSG calls ActivateInputField() and Select() before the transaction is finished
|
||||||
|
// During the transaction, the whole canvas group is not interactable, and these methods don't work on non-interactable fields
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(TMP_InputField ____quantity)
|
||||||
|
{
|
||||||
|
____quantity.ActivateInputField();
|
||||||
|
____quantity.Select();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,117 +9,116 @@ using TMPro;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class GPCoinPatches
|
||||||
{
|
{
|
||||||
public static class GPCoinPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new MoneyPanelPatch().Enable();
|
||||||
|
new MoneyPanelTMPTextPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MoneyPanelPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new MoneyPanelPatch().Enable();
|
return AccessTools.Method(typeof(DisplayMoneyPanel), nameof(DisplayMoneyPanel.Show));
|
||||||
new MoneyPanelTMPTextPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MoneyPanelPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(DisplayMoneyPanel __instance, IEnumerable<Item> inventoryItems, TextMeshProUGUI ____roubles, TextMeshProUGUI ____euros, TextMeshProUGUI ____dollars)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.ShowGPCurrency.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(DisplayMoneyPanel), nameof(DisplayMoneyPanel.Show));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
__instance.ShowGameObject();
|
||||||
public static bool Prefix(DisplayMoneyPanel __instance, IEnumerable<Item> inventoryItems, TextMeshProUGUI ____roubles, TextMeshProUGUI ____euros, TextMeshProUGUI ____dollars)
|
|
||||||
|
Transform gpCoinsTransform = __instance.transform.Find("GPCoins");
|
||||||
|
if (gpCoinsTransform == null)
|
||||||
{
|
{
|
||||||
if (!Settings.ShowGPCurrency.Value)
|
Transform dollars = __instance.transform.Find("Dollars");
|
||||||
|
gpCoinsTransform = UnityEngine.Object.Instantiate(dollars, __instance.transform, false);
|
||||||
|
gpCoinsTransform.name = "GPCoins";
|
||||||
|
|
||||||
|
Image icon = gpCoinsTransform.Find("Image").GetComponent<Image>();
|
||||||
|
icon.sprite = EFTHardSettings.Instance.StaticIcons.GetSmallCurrencySign(ECurrencyType.GP);
|
||||||
|
|
||||||
|
LayoutElement imageLayout = icon.GetComponent<LayoutElement>();
|
||||||
|
imageLayout.preferredHeight = -1f;
|
||||||
|
imageLayout.preferredWidth = -1f;
|
||||||
|
|
||||||
|
Settings.ShowGPCurrency.Subscribe(enabled =>
|
||||||
{
|
{
|
||||||
return true;
|
if (!enabled && gpCoinsTransform != null)
|
||||||
}
|
|
||||||
|
|
||||||
__instance.ShowGameObject();
|
|
||||||
|
|
||||||
Transform gpCoinsTransform = __instance.transform.Find("GPCoins");
|
|
||||||
if (gpCoinsTransform == null)
|
|
||||||
{
|
|
||||||
Transform dollars = __instance.transform.Find("Dollars");
|
|
||||||
gpCoinsTransform = UnityEngine.Object.Instantiate(dollars, __instance.transform, false);
|
|
||||||
gpCoinsTransform.name = "GPCoins";
|
|
||||||
|
|
||||||
Image icon = gpCoinsTransform.Find("Image").GetComponent<Image>();
|
|
||||||
icon.sprite = EFTHardSettings.Instance.StaticIcons.GetSmallCurrencySign(ECurrencyType.GP);
|
|
||||||
|
|
||||||
LayoutElement imageLayout = icon.GetComponent<LayoutElement>();
|
|
||||||
imageLayout.preferredHeight = -1f;
|
|
||||||
imageLayout.preferredWidth = -1f;
|
|
||||||
|
|
||||||
Settings.ShowGPCurrency.Subscribe(enabled =>
|
|
||||||
{
|
{
|
||||||
if (!enabled && gpCoinsTransform != null)
|
UnityEngine.Object.Destroy(gpCoinsTransform.gameObject);
|
||||||
{
|
}
|
||||||
UnityEngine.Object.Destroy(gpCoinsTransform.gameObject);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMeshProUGUI gpCoins = gpCoinsTransform.Find("Label").GetComponent<TextMeshProUGUI>();
|
|
||||||
|
|
||||||
var sums = R.Money.GetMoneySums(inventoryItems);
|
|
||||||
|
|
||||||
NumberFormatInfo numberFormatInfo = new() { NumberGroupSeparator = " " };
|
|
||||||
|
|
||||||
____roubles.text = sums[ECurrencyType.RUB].ToString("N0", numberFormatInfo);
|
|
||||||
____euros.text = sums[ECurrencyType.EUR].ToString("N0", numberFormatInfo);
|
|
||||||
____dollars.text = sums[ECurrencyType.USD].ToString("N0", numberFormatInfo);
|
|
||||||
gpCoins.text = sums[ECurrencyType.GP].ToString("N0", numberFormatInfo);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextMeshProUGUI gpCoins = gpCoinsTransform.Find("Label").GetComponent<TextMeshProUGUI>();
|
||||||
|
|
||||||
|
var sums = R.Money.GetMoneySums(inventoryItems);
|
||||||
|
|
||||||
|
NumberFormatInfo numberFormatInfo = new() { NumberGroupSeparator = " " };
|
||||||
|
|
||||||
|
____roubles.text = sums[ECurrencyType.RUB].ToString("N0", numberFormatInfo);
|
||||||
|
____euros.text = sums[ECurrencyType.EUR].ToString("N0", numberFormatInfo);
|
||||||
|
____dollars.text = sums[ECurrencyType.USD].ToString("N0", numberFormatInfo);
|
||||||
|
gpCoins.text = sums[ECurrencyType.GP].ToString("N0", numberFormatInfo);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MoneyPanelTMPTextPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(DisplayMoneyPanelTMPText), nameof(DisplayMoneyPanelTMPText.Show));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MoneyPanelTMPTextPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(DisplayMoneyPanelTMPText __instance, IEnumerable<Item> inventoryItems, TMP_Text ____roubles, TMP_Text ____euros, TMP_Text ____dollars)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.ShowGPCurrency.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(DisplayMoneyPanelTMPText), nameof(DisplayMoneyPanelTMPText.Show));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
__instance.ShowGameObject();
|
||||||
public static bool Prefix(DisplayMoneyPanelTMPText __instance, IEnumerable<Item> inventoryItems, TMP_Text ____roubles, TMP_Text ____euros, TMP_Text ____dollars)
|
|
||||||
|
Transform gpCoinsTransform = __instance.transform.Find("GP");
|
||||||
|
if (gpCoinsTransform == null)
|
||||||
{
|
{
|
||||||
if (!Settings.ShowGPCurrency.Value)
|
Transform dollars = __instance.transform.Find("USD");
|
||||||
|
gpCoinsTransform = UnityEngine.Object.Instantiate(dollars, __instance.transform, false);
|
||||||
|
gpCoinsTransform.name = "GP";
|
||||||
|
|
||||||
|
Settings.ShowGPCurrency.Subscribe(enabled =>
|
||||||
{
|
{
|
||||||
return true;
|
if (!enabled && gpCoinsTransform != null)
|
||||||
}
|
|
||||||
|
|
||||||
__instance.ShowGameObject();
|
|
||||||
|
|
||||||
Transform gpCoinsTransform = __instance.transform.Find("GP");
|
|
||||||
if (gpCoinsTransform == null)
|
|
||||||
{
|
|
||||||
Transform dollars = __instance.transform.Find("USD");
|
|
||||||
gpCoinsTransform = UnityEngine.Object.Instantiate(dollars, __instance.transform, false);
|
|
||||||
gpCoinsTransform.name = "GP";
|
|
||||||
|
|
||||||
Settings.ShowGPCurrency.Subscribe(enabled =>
|
|
||||||
{
|
{
|
||||||
if (!enabled && gpCoinsTransform != null)
|
UnityEngine.Object.Destroy(gpCoinsTransform.gameObject);
|
||||||
{
|
}
|
||||||
UnityEngine.Object.Destroy(gpCoinsTransform.gameObject);
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMeshProUGUI gpCoins = gpCoinsTransform.GetComponent<TextMeshProUGUI>();
|
|
||||||
|
|
||||||
var sums = R.Money.GetMoneySums(inventoryItems);
|
|
||||||
|
|
||||||
NumberFormatInfo numberFormatInfo = new() { NumberGroupSeparator = " " };
|
|
||||||
|
|
||||||
____roubles.text = CurrencyInfo.GetCurrencyChar(ECurrencyType.RUB) + " " + sums[ECurrencyType.RUB].ToString("N0", numberFormatInfo);
|
|
||||||
____euros.text = CurrencyInfo.GetCurrencyChar(ECurrencyType.EUR) + " " + sums[ECurrencyType.EUR].ToString("N0", numberFormatInfo);
|
|
||||||
____dollars.text = CurrencyInfo.GetCurrencyChar(ECurrencyType.USD) + " " + sums[ECurrencyType.USD].ToString("N0", numberFormatInfo);
|
|
||||||
gpCoins.text = "GP " + sums[ECurrencyType.GP].ToString("N0", numberFormatInfo);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextMeshProUGUI gpCoins = gpCoinsTransform.GetComponent<TextMeshProUGUI>();
|
||||||
|
|
||||||
|
var sums = R.Money.GetMoneySums(inventoryItems);
|
||||||
|
|
||||||
|
NumberFormatInfo numberFormatInfo = new() { NumberGroupSeparator = " " };
|
||||||
|
|
||||||
|
____roubles.text = CurrencyInfo.GetCurrencyChar(ECurrencyType.RUB) + " " + sums[ECurrencyType.RUB].ToString("N0", numberFormatInfo);
|
||||||
|
____euros.text = CurrencyInfo.GetCurrencyChar(ECurrencyType.EUR) + " " + sums[ECurrencyType.EUR].ToString("N0", numberFormatInfo);
|
||||||
|
____dollars.text = CurrencyInfo.GetCurrencyChar(ECurrencyType.USD) + " " + sums[ECurrencyType.USD].ToString("N0", numberFormatInfo);
|
||||||
|
gpCoins.text = "GP " + sums[ECurrencyType.GP].ToString("N0", numberFormatInfo);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,96 +6,95 @@ using System.Reflection;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class GridWindowButtonsPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class GridWindowButtonsPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.DeclaredMethod(typeof(GridWindow), nameof(GridWindow.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(GridWindow __instance)
|
||||||
|
{
|
||||||
|
var wrappedInstance = __instance.R();
|
||||||
|
if (Settings.AddContainerButtons.Value && wrappedInstance.LootItem.Int32_0 > 3) // Greater than 3 cells wide
|
||||||
{
|
{
|
||||||
return AccessTools.DeclaredMethod(typeof(GridWindow), nameof(GridWindow.Show));
|
Transform closeButton = __instance.transform.Find("Caption Panel/Close Button");
|
||||||
|
Image sortBackground = __instance.transform.Find("Caption Panel/Sort Button")?.GetComponent<Image>();
|
||||||
|
|
||||||
|
// Left button
|
||||||
|
Button leftButton = CreateButton(closeButton, sortBackground.sprite, EItemAttributeId.RecoilBack);
|
||||||
|
leftButton.onClick.AddListener(() => SnapLeft(__instance));
|
||||||
|
wrappedInstance.UI.AddDisposable(() => leftButton.onClick.RemoveAllListeners());
|
||||||
|
|
||||||
|
// Right button
|
||||||
|
Button rightButton = CreateButton(closeButton, sortBackground.sprite, EItemAttributeId.RecoilBack);
|
||||||
|
rightButton.transform.Find("X").Rotate(0f, 180f, 0f);
|
||||||
|
rightButton.onClick.AddListener(() => SnapRight(__instance));
|
||||||
|
wrappedInstance.UI.AddDisposable(() => rightButton.onClick.RemoveAllListeners());
|
||||||
|
|
||||||
|
// Put close back on the end
|
||||||
|
closeButton.SetAsLastSibling();
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
// Keybinds
|
||||||
public static void Postfix(GridWindow __instance)
|
LeftRightKeybind leftRightKeybind = __instance.GetOrAddComponent<LeftRightKeybind>();
|
||||||
|
leftRightKeybind.Init(__instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Button CreateButton(Transform template, Sprite backgroundSprite, EItemAttributeId attributeIcon)
|
||||||
|
{
|
||||||
|
Transform transform = UnityEngine.Object.Instantiate(template, template.parent, false);
|
||||||
|
|
||||||
|
Image background = transform.GetComponent<Image>();
|
||||||
|
background.sprite = backgroundSprite;
|
||||||
|
|
||||||
|
Image icon = transform.Find("X").GetComponent<Image>();
|
||||||
|
icon.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(attributeIcon);
|
||||||
|
icon.overrideSprite = null;
|
||||||
|
icon.SetNativeSize();
|
||||||
|
|
||||||
|
Button button = transform.GetComponent<Button>();
|
||||||
|
button.navigation = new Navigation() { mode = Navigation.Mode.None };
|
||||||
|
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LeftRightKeybind : MonoBehaviour
|
||||||
|
{
|
||||||
|
private GridWindow window;
|
||||||
|
|
||||||
|
public void Init(GridWindow window)
|
||||||
{
|
{
|
||||||
var wrappedInstance = __instance.R();
|
this.window = window;
|
||||||
if (Settings.AddContainerButtons.Value && wrappedInstance.LootItem.Int32_0 > 3) // Greater than 3 cells wide
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
bool isTopWindow = window.transform.GetSiblingIndex() == window.transform.parent.childCount - 1;
|
||||||
|
if (Settings.SnapLeftKeybind.Value.IsDown() && isTopWindow)
|
||||||
{
|
{
|
||||||
Transform closeButton = __instance.transform.Find("Caption Panel/Close Button");
|
SnapLeft(window);
|
||||||
Image sortBackground = __instance.transform.Find("Caption Panel/Sort Button")?.GetComponent<Image>();
|
|
||||||
|
|
||||||
// Left button
|
|
||||||
Button leftButton = CreateButton(closeButton, sortBackground.sprite, EItemAttributeId.RecoilBack);
|
|
||||||
leftButton.onClick.AddListener(() => SnapLeft(__instance));
|
|
||||||
wrappedInstance.UI.AddDisposable(() => leftButton.onClick.RemoveAllListeners());
|
|
||||||
|
|
||||||
// Right button
|
|
||||||
Button rightButton = CreateButton(closeButton, sortBackground.sprite, EItemAttributeId.RecoilBack);
|
|
||||||
rightButton.transform.Find("X").Rotate(0f, 180f, 0f);
|
|
||||||
rightButton.onClick.AddListener(() => SnapRight(__instance));
|
|
||||||
wrappedInstance.UI.AddDisposable(() => rightButton.onClick.RemoveAllListeners());
|
|
||||||
|
|
||||||
// Put close back on the end
|
|
||||||
closeButton.SetAsLastSibling();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keybinds
|
if (Settings.SnapRightKeybind.Value.IsDown() && isTopWindow)
|
||||||
LeftRightKeybind leftRightKeybind = __instance.GetOrAddComponent<LeftRightKeybind>();
|
|
||||||
leftRightKeybind.Init(__instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Button CreateButton(Transform template, Sprite backgroundSprite, EItemAttributeId attributeIcon)
|
|
||||||
{
|
|
||||||
Transform transform = UnityEngine.Object.Instantiate(template, template.parent, false);
|
|
||||||
|
|
||||||
Image background = transform.GetComponent<Image>();
|
|
||||||
background.sprite = backgroundSprite;
|
|
||||||
|
|
||||||
Image icon = transform.Find("X").GetComponent<Image>();
|
|
||||||
icon.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(attributeIcon);
|
|
||||||
icon.overrideSprite = null;
|
|
||||||
icon.SetNativeSize();
|
|
||||||
|
|
||||||
Button button = transform.GetComponent<Button>();
|
|
||||||
button.navigation = new Navigation() { mode = Navigation.Mode.None };
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LeftRightKeybind : MonoBehaviour
|
|
||||||
{
|
|
||||||
private GridWindow window;
|
|
||||||
|
|
||||||
public void Init(GridWindow window)
|
|
||||||
{
|
{
|
||||||
this.window = window;
|
SnapRight(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
bool isTopWindow = window.transform.GetSiblingIndex() == window.transform.parent.childCount - 1;
|
|
||||||
if (Settings.SnapLeftKeybind.Value.IsDown() && isTopWindow)
|
|
||||||
{
|
|
||||||
SnapLeft(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SnapRightKeybind.Value.IsDown() && isTopWindow)
|
|
||||||
{
|
|
||||||
SnapRight(window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SnapLeft(GridWindow window)
|
|
||||||
{
|
|
||||||
RectTransform inspectRect = (RectTransform)window.transform;
|
|
||||||
inspectRect.anchoredPosition = new Vector2((float)Screen.width / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SnapRight(GridWindow window)
|
|
||||||
{
|
|
||||||
RectTransform inspectRect = (RectTransform)window.transform;
|
|
||||||
inspectRect.anchoredPosition = new Vector2((float)Screen.width * 3f / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SnapLeft(GridWindow window)
|
||||||
|
{
|
||||||
|
RectTransform inspectRect = (RectTransform)window.transform;
|
||||||
|
inspectRect.anchoredPosition = new Vector2((float)Screen.width / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SnapRight(GridWindow window)
|
||||||
|
{
|
||||||
|
RectTransform inspectRect = (RectTransform)window.transform;
|
||||||
|
inspectRect.anchoredPosition = new Vector2((float)Screen.width * 3f / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,85 +3,85 @@ using HarmonyLib;
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class HideoutLevelPatches
|
||||||
{
|
{
|
||||||
public static class HideoutLevelPatches
|
private static string CurrentArea;
|
||||||
|
private static ELevelType CurrentLevel = ELevelType.NotSet;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static string CurrentArea;
|
new SelectAreaPatch().Enable();
|
||||||
private static ELevelType CurrentLevel = ELevelType.NotSet;
|
new ChangeLevelPatch().Enable();
|
||||||
|
new PickInitialLevelPatch().Enable();
|
||||||
|
new ClearLevelPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
public class SelectAreaPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new SelectAreaPatch().Enable();
|
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.SelectArea));
|
||||||
new ChangeLevelPatch().Enable();
|
|
||||||
new PickInitialLevelPatch().Enable();
|
|
||||||
new ClearLevelPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SelectAreaPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix(AreaData areaData)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (areaData.Template.Id != CurrentArea)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.SelectArea));
|
CurrentArea = areaData.Template.Id;
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(AreaData areaData)
|
|
||||||
{
|
|
||||||
if (areaData.Template.Id != CurrentArea)
|
|
||||||
{
|
|
||||||
CurrentArea = areaData.Template.Id;
|
|
||||||
CurrentLevel = ELevelType.NotSet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChangeLevelPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.method_6));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(ELevelType state)
|
|
||||||
{
|
|
||||||
CurrentLevel = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PickInitialLevelPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.method_3));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(ref ELevelType __result)
|
|
||||||
{
|
|
||||||
if (CurrentLevel != ELevelType.NotSet) {
|
|
||||||
__result = CurrentLevel;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ClearLevelPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(HideoutScreenOverlay), nameof(HideoutScreenOverlay.ReturnToPreviousState));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
CurrentArea = null;
|
|
||||||
CurrentLevel = ELevelType.NotSet;
|
CurrentLevel = ELevelType.NotSet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ChangeLevelPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.method_6));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(ELevelType state)
|
||||||
|
{
|
||||||
|
CurrentLevel = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PickInitialLevelPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.method_3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(ref ELevelType __result)
|
||||||
|
{
|
||||||
|
if (CurrentLevel != ELevelType.NotSet)
|
||||||
|
{
|
||||||
|
__result = CurrentLevel;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ClearLevelPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(HideoutScreenOverlay), nameof(HideoutScreenOverlay.ReturnToPreviousState));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
CurrentArea = null;
|
||||||
|
CurrentLevel = ELevelType.NotSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,228 +11,227 @@ using TMPro;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class HideoutSearchPatches
|
||||||
{
|
{
|
||||||
public static class HideoutSearchPatches
|
private static readonly Dictionary<string, string> LastSearches = [];
|
||||||
|
|
||||||
|
private static float LastAbsoluteDownScrollPosition = -1f;
|
||||||
|
|
||||||
|
private static void ClearLastScrollPosition() => LastAbsoluteDownScrollPosition = -1f;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, string> LastSearches = [];
|
new LazyLoadPatch().Enable();
|
||||||
|
new RestoreHideoutSearchPatch().Enable();
|
||||||
|
new SaveHideoutSearchPatch().Enable();
|
||||||
|
new CloseHideoutSearchPatch().Enable();
|
||||||
|
new FastHideoutSearchPatch().Enable();
|
||||||
|
new FixHideoutSearchAgainPatch().Enable();
|
||||||
|
new CancelScrollOnMouseWheelPatch().Enable();
|
||||||
|
new BlockHideoutEnterPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
private static float LastAbsoluteDownScrollPosition = -1f;
|
// Deactivate ProduceViews as they lazy load if they don't match the search
|
||||||
|
public class LazyLoadPatch : ModulePatch
|
||||||
private static void ClearLastScrollPosition() => LastAbsoluteDownScrollPosition = -1f;
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
public static void Enable()
|
|
||||||
{
|
{
|
||||||
new LazyLoadPatch().Enable();
|
return AccessTools.Method(R.ProductionPanelShowSubclass.Type, "method_2");
|
||||||
new RestoreHideoutSearchPatch().Enable();
|
|
||||||
new SaveHideoutSearchPatch().Enable();
|
|
||||||
new CloseHideoutSearchPatch().Enable();
|
|
||||||
new FastHideoutSearchPatch().Enable();
|
|
||||||
new FixHideoutSearchAgainPatch().Enable();
|
|
||||||
new CancelScrollOnMouseWheelPatch().Enable();
|
|
||||||
new BlockHideoutEnterPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deactivate ProduceViews as they lazy load if they don't match the search
|
[PatchPostfix]
|
||||||
public class LazyLoadPatch : ModulePatch
|
public static void Postfix(object __instance, object scheme, ProduceView view)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var instance = new R.ProductionPanelShowSubclass(__instance);
|
||||||
|
var productScheme = new R.Scheme(scheme);
|
||||||
|
|
||||||
|
ValidationInputField searchField = instance.ProductionPanel.R().SeachInputField;
|
||||||
|
if (searchField.text.Length > 0 && productScheme.EndProduct.LocalizedName().IndexOf(searchField.text, StringComparison.InvariantCultureIgnoreCase) < 0)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(R.ProductionPanelShowSubclass.Type, "method_2");
|
view.GameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
// As the objects load in, try to restore the old scroll position
|
||||||
public static void Postfix(object __instance, object scheme, ProduceView view)
|
if (LastAbsoluteDownScrollPosition >= 0f)
|
||||||
{
|
{
|
||||||
var instance = new R.ProductionPanelShowSubclass(__instance);
|
ScrollRect scrollRect = view.GetComponentInParent<ScrollRect>();
|
||||||
var productScheme = new R.Scheme(scheme);
|
|
||||||
|
|
||||||
ValidationInputField searchField = instance.ProductionPanel.R().SeachInputField;
|
|
||||||
if (searchField.text.Length > 0 && productScheme.EndProduct.LocalizedName().IndexOf(searchField.text, StringComparison.InvariantCultureIgnoreCase) < 0)
|
|
||||||
{
|
|
||||||
view.GameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// As the objects load in, try to restore the old scroll position
|
|
||||||
if (LastAbsoluteDownScrollPosition >= 0f)
|
|
||||||
{
|
|
||||||
ScrollRect scrollRect = view.GetComponentInParent<ScrollRect>();
|
|
||||||
if (scrollRect != null)
|
|
||||||
{
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(scrollRect.RectTransform());
|
|
||||||
float currentAbsoluteDownScrollPosition = (1f - scrollRect.verticalNormalizedPosition) * (scrollRect.content.rect.height - scrollRect.viewport.rect.height);
|
|
||||||
if (LastAbsoluteDownScrollPosition > currentAbsoluteDownScrollPosition + 112f) // 112 is about the height of each item
|
|
||||||
{
|
|
||||||
scrollRect.verticalNormalizedPosition = 0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Last one, try to set it exactly
|
|
||||||
scrollRect.verticalNormalizedPosition = 1f - (LastAbsoluteDownScrollPosition / (scrollRect.content.rect.height - scrollRect.viewport.rect.height));
|
|
||||||
ClearLastScrollPosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate the search box, and force the window to render
|
|
||||||
public class RestoreHideoutSearchPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.ShowContents));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(ProductionPanel __instance, ValidationInputField ____searchInputField)
|
|
||||||
{
|
|
||||||
if (LastSearches.TryGetValue(__instance.AreaData.ToString(), out string lastSearch))
|
|
||||||
{
|
|
||||||
____searchInputField.text = lastSearch;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollPatches.KeyScrollListener listener = __instance.GetComponentInParent<ScrollPatches.KeyScrollListener>();
|
|
||||||
listener?.OnKeyScroll.AddListener(ClearLastScrollPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ProductionPanel __instance, ValidationInputField ____searchInputField)
|
|
||||||
{
|
|
||||||
// Force it to render immediately, at full height, even if the search filtering would reduce the number of children
|
|
||||||
if (__instance.method_9().Count() > 2)
|
|
||||||
{
|
|
||||||
AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent<AreaScreenSubstrate>();
|
|
||||||
LayoutElement layoutElement = areaScreenSubstrate.R().ContentLayout;
|
|
||||||
layoutElement.minHeight = 750f; // aka areaScreenSubstrate._maxHeight
|
|
||||||
areaScreenSubstrate.method_8();
|
|
||||||
}
|
|
||||||
|
|
||||||
____searchInputField.ActivateInputField();
|
|
||||||
____searchInputField.Select();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// method_9 gets the sorted list of products. If there's a search term, prioritize the matching items so they load first
|
|
||||||
public class FastHideoutSearchPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.method_9));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied directly from method_9
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(ProductionPanel __instance, ref IEnumerable<Scheme> __result, ValidationInputField ____searchInputField)
|
|
||||||
{
|
|
||||||
__result = __instance.R().ProductionBuilds.OfType<Scheme>().Where(scheme => !scheme.locked)
|
|
||||||
.OrderBy(scheme => scheme.endProduct.LocalizedName().Contains(____searchInputField.text) ? 0 : 1) // search-matching items first
|
|
||||||
.ThenBy(__instance.method_18)
|
|
||||||
.ThenBy(scheme => scheme.FavoriteIndex)
|
|
||||||
.ThenBy(scheme => scheme.Level);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// method_14 activates/deactivates the product game objects based on the search. Need to resort the list due to above patch
|
|
||||||
public class FixHideoutSearchAgainPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.method_14));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(ProductionPanel __instance)
|
|
||||||
{
|
|
||||||
__instance.method_13(); // update sort order
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the search as the window closes
|
|
||||||
public class SaveHideoutSearchPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.Close));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(ProductionPanel __instance, ValidationInputField ____searchInputField)
|
|
||||||
{
|
|
||||||
LastSearches[__instance.AreaData.ToString()] = ____searchInputField.text;
|
|
||||||
|
|
||||||
ScrollRect scrollRect = __instance.GetComponentInParent<ScrollRect>();
|
|
||||||
if (scrollRect != null)
|
if (scrollRect != null)
|
||||||
{
|
{
|
||||||
if (Settings.RestoreAsyncScrollPositions.Value)
|
LayoutRebuilder.ForceRebuildLayoutImmediate(scrollRect.RectTransform());
|
||||||
|
float currentAbsoluteDownScrollPosition = (1f - scrollRect.verticalNormalizedPosition) * (scrollRect.content.rect.height - scrollRect.viewport.rect.height);
|
||||||
|
if (LastAbsoluteDownScrollPosition > currentAbsoluteDownScrollPosition + 112f) // 112 is about the height of each item
|
||||||
{
|
{
|
||||||
// Need to save the absolute DOWN position, because that's the direction the scrollbox will grow.
|
scrollRect.verticalNormalizedPosition = 0f;
|
||||||
// Subtract the viewport height from content heigh because that's the actual RANGE of the scroll position
|
}
|
||||||
LastAbsoluteDownScrollPosition = (1f - scrollRect.verticalNormalizedPosition) * (scrollRect.content.rect.height - scrollRect.viewport.rect.height);
|
else
|
||||||
|
{
|
||||||
|
// Last one, try to set it exactly
|
||||||
|
scrollRect.verticalNormalizedPosition = 1f - (LastAbsoluteDownScrollPosition / (scrollRect.content.rect.height - scrollRect.viewport.rect.height));
|
||||||
|
ClearLastScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollRect.GetComponent<ScrollPatches.KeyScrollListener>()?.OnKeyScroll.RemoveListener(ClearLastScrollPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the default behavior
|
|
||||||
AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent<AreaScreenSubstrate>();
|
|
||||||
LayoutElement layoutElement = areaScreenSubstrate.R().ContentLayout;
|
|
||||||
layoutElement.minHeight = -1f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the search stuff when you exit out
|
|
||||||
public class CloseHideoutSearchPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(HideoutScreenOverlay), nameof(HideoutScreenOverlay.ReturnToPreviousState));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
LastSearches.Clear();
|
|
||||||
ClearLastScrollPosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CancelScrollOnMouseWheelPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ScrollRectNoDrag), nameof(ScrollRectNoDrag.OnScroll));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
ClearLastScrollPosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BlockHideoutEnterPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(HideoutScreenOverlay), nameof(HideoutScreenOverlay.TranslateCommand));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(ECommand command, ref InputNode.ETranslateResult __result)
|
|
||||||
{
|
|
||||||
if (command == ECommand.Enter &&
|
|
||||||
EventSystem.current?.currentSelectedGameObject != null &&
|
|
||||||
EventSystem.current.currentSelectedGameObject.GetComponent<TMP_InputField>() != null)
|
|
||||||
{
|
|
||||||
__result = InputNode.ETranslateResult.Block;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populate the search box, and force the window to render
|
||||||
|
public class RestoreHideoutSearchPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.ShowContents));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(ProductionPanel __instance, ValidationInputField ____searchInputField)
|
||||||
|
{
|
||||||
|
if (LastSearches.TryGetValue(__instance.AreaData.ToString(), out string lastSearch))
|
||||||
|
{
|
||||||
|
____searchInputField.text = lastSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollPatches.KeyScrollListener listener = __instance.GetComponentInParent<ScrollPatches.KeyScrollListener>();
|
||||||
|
listener?.OnKeyScroll.AddListener(ClearLastScrollPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ProductionPanel __instance, ValidationInputField ____searchInputField)
|
||||||
|
{
|
||||||
|
// Force it to render immediately, at full height, even if the search filtering would reduce the number of children
|
||||||
|
if (__instance.method_9().Count() > 2)
|
||||||
|
{
|
||||||
|
AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent<AreaScreenSubstrate>();
|
||||||
|
LayoutElement layoutElement = areaScreenSubstrate.R().ContentLayout;
|
||||||
|
layoutElement.minHeight = 750f; // aka areaScreenSubstrate._maxHeight
|
||||||
|
areaScreenSubstrate.method_8();
|
||||||
|
}
|
||||||
|
|
||||||
|
____searchInputField.ActivateInputField();
|
||||||
|
____searchInputField.Select();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method_9 gets the sorted list of products. If there's a search term, prioritize the matching items so they load first
|
||||||
|
public class FastHideoutSearchPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.method_9));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied directly from method_9
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(ProductionPanel __instance, ref IEnumerable<Scheme> __result, ValidationInputField ____searchInputField)
|
||||||
|
{
|
||||||
|
__result = __instance.R().ProductionBuilds.OfType<Scheme>().Where(scheme => !scheme.locked)
|
||||||
|
.OrderBy(scheme => scheme.endProduct.LocalizedName().Contains(____searchInputField.text) ? 0 : 1) // search-matching items first
|
||||||
|
.ThenBy(__instance.method_18)
|
||||||
|
.ThenBy(scheme => scheme.FavoriteIndex)
|
||||||
|
.ThenBy(scheme => scheme.Level);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method_14 activates/deactivates the product game objects based on the search. Need to resort the list due to above patch
|
||||||
|
public class FixHideoutSearchAgainPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.method_14));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(ProductionPanel __instance)
|
||||||
|
{
|
||||||
|
__instance.method_13(); // update sort order
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the search as the window closes
|
||||||
|
public class SaveHideoutSearchPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ProductionPanel), nameof(ProductionPanel.Close));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(ProductionPanel __instance, ValidationInputField ____searchInputField)
|
||||||
|
{
|
||||||
|
LastSearches[__instance.AreaData.ToString()] = ____searchInputField.text;
|
||||||
|
|
||||||
|
ScrollRect scrollRect = __instance.GetComponentInParent<ScrollRect>();
|
||||||
|
if (scrollRect != null)
|
||||||
|
{
|
||||||
|
if (Settings.RestoreAsyncScrollPositions.Value)
|
||||||
|
{
|
||||||
|
// Need to save the absolute DOWN position, because that's the direction the scrollbox will grow.
|
||||||
|
// Subtract the viewport height from content heigh because that's the actual RANGE of the scroll position
|
||||||
|
LastAbsoluteDownScrollPosition = (1f - scrollRect.verticalNormalizedPosition) * (scrollRect.content.rect.height - scrollRect.viewport.rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollRect.GetComponent<ScrollPatches.KeyScrollListener>()?.OnKeyScroll.RemoveListener(ClearLastScrollPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the default behavior
|
||||||
|
AreaScreenSubstrate areaScreenSubstrate = __instance.GetComponentInParent<AreaScreenSubstrate>();
|
||||||
|
LayoutElement layoutElement = areaScreenSubstrate.R().ContentLayout;
|
||||||
|
layoutElement.minHeight = -1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the search stuff when you exit out
|
||||||
|
public class CloseHideoutSearchPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(HideoutScreenOverlay), nameof(HideoutScreenOverlay.ReturnToPreviousState));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
LastSearches.Clear();
|
||||||
|
ClearLastScrollPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CancelScrollOnMouseWheelPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ScrollRectNoDrag), nameof(ScrollRectNoDrag.OnScroll));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
ClearLastScrollPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlockHideoutEnterPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(HideoutScreenOverlay), nameof(HideoutScreenOverlay.TranslateCommand));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(ECommand command, ref InputNode.ETranslateResult __result)
|
||||||
|
{
|
||||||
|
if (command == ECommand.Enter &&
|
||||||
|
EventSystem.current?.currentSelectedGameObject != null &&
|
||||||
|
EventSystem.current.currentSelectedGameObject.GetComponent<TMP_InputField>() != null)
|
||||||
|
{
|
||||||
|
__result = InputNode.ETranslateResult.Block;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,273 +9,271 @@ using System.Reflection;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
internal static class InspectWindowResizePatches
|
||||||
{
|
{
|
||||||
internal static class InspectWindowResizePatches
|
private static float SavedPreferredWidth = -1f;
|
||||||
|
private static float SavedPreferredHeight = -1f;
|
||||||
|
|
||||||
|
// Seems like this is the always the default for ItemSpecificationPanels
|
||||||
|
private const float DefaultPreferredWidth = 670f;
|
||||||
|
private const float DefaultPreferredHeight = 500f;
|
||||||
|
|
||||||
|
private const float ButtonPadding = 3f;
|
||||||
|
|
||||||
|
private static Image ButtonBackground; // Nice gray background for the new buttons
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static float SavedPreferredWidth = -1f;
|
new SaveInspectWindowSizePatch().Enable();
|
||||||
private static float SavedPreferredHeight = -1f;
|
new AddInspectWindowButtonsPatch().Enable();
|
||||||
|
new GrowInspectWindowDescriptionPatch().Enable();
|
||||||
|
new LeftRightKeybindsPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
// Seems like this is the always the default for ItemSpecificationPanels
|
public class SaveInspectWindowSizePatch : ModulePatch
|
||||||
private const float DefaultPreferredWidth = 670f;
|
{
|
||||||
private const float DefaultPreferredHeight = 500f;
|
protected override MethodBase GetTargetMethod()
|
||||||
|
|
||||||
private const float ButtonPadding = 3f;
|
|
||||||
|
|
||||||
private static Image ButtonBackground; // Nice gray background for the new buttons
|
|
||||||
|
|
||||||
public static void Enable()
|
|
||||||
{
|
{
|
||||||
new SaveInspectWindowSizePatch().Enable();
|
return AccessTools.Method(typeof(StretchArea), nameof(StretchArea.OnDrag));
|
||||||
new AddInspectWindowButtonsPatch().Enable();
|
|
||||||
new GrowInspectWindowDescriptionPatch().Enable();
|
|
||||||
new LeftRightKeybindsPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SaveInspectWindowSizePatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(LayoutElement ___layoutElement_0)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.RememberInspectSize.Value || ___layoutElement_0.GetComponent<ItemSpecificationPanel>() == null)
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(StretchArea), nameof(StretchArea.OnDrag));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(LayoutElement ___layoutElement_0)
|
|
||||||
{
|
|
||||||
if (!Settings.RememberInspectSize.Value || ___layoutElement_0.GetComponent<ItemSpecificationPanel>() == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SavedPreferredWidth = ___layoutElement_0.preferredWidth;
|
|
||||||
SavedPreferredHeight = ___layoutElement_0.preferredHeight;
|
|
||||||
|
|
||||||
Button resizeButton = ___layoutElement_0.transform.Find("Inner/Caption Panel/Restore")?.GetComponent<Button>();
|
|
||||||
if (resizeButton != null && !resizeButton.IsActive())
|
|
||||||
{
|
|
||||||
resizeButton.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddInspectWindowButtonsPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(LayoutElement ___layoutElement_0)
|
|
||||||
{
|
|
||||||
if (Settings.RememberInspectSize.Value)
|
|
||||||
{
|
|
||||||
RestoreSavedSize(___layoutElement_0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ItemSpecificationPanel __instance, LayoutElement ___layoutElement_0, ItemUiContext ___itemUiContext_0)
|
|
||||||
{
|
|
||||||
if (Settings.LockInspectPreviewSize.Value)
|
|
||||||
{
|
|
||||||
LayoutElement previewPanel = __instance.transform.Find("Inner/Contents/Preview Panel")?.GetComponent<LayoutElement>();
|
|
||||||
if (previewPanel != null)
|
|
||||||
{
|
|
||||||
previewPanel.flexibleHeight = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ButtonBackground == null)
|
|
||||||
{
|
|
||||||
// Steal the background image fom gridwindow sort
|
|
||||||
ButtonBackground = ___itemUiContext_0.R().GridWindowTemplate.R().GridSortPanel.R().Button.image;
|
|
||||||
}
|
|
||||||
|
|
||||||
Button closeButton = __instance.transform.Find("Inner/Caption Panel/Close Button")?.GetComponent<Button>();
|
|
||||||
if (closeButton != null)
|
|
||||||
{
|
|
||||||
CreateRightButton(__instance, closeButton);
|
|
||||||
CreateLeftButton(__instance, closeButton);
|
|
||||||
CreateRestoreButton(__instance, ___layoutElement_0, closeButton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateRestoreButton(ItemSpecificationPanel inspectPanel, LayoutElement inspectLayout, Button template)
|
|
||||||
{
|
|
||||||
RectTransform templateRect = (RectTransform)template.transform;
|
|
||||||
|
|
||||||
Button restoreButton = UnityEngine.Object.Instantiate(template, template.transform.parent, false);
|
|
||||||
restoreButton.name = "Restore";
|
|
||||||
restoreButton.navigation = new Navigation() { mode = Navigation.Mode.None };
|
|
||||||
|
|
||||||
RectTransform restoreRect = (RectTransform)restoreButton.transform;
|
|
||||||
restoreRect.localPosition = new Vector3(templateRect.localPosition.x - 3 * (templateRect.rect.width + ButtonPadding), templateRect.localPosition.y, templateRect.localPosition.z);
|
|
||||||
|
|
||||||
Image background = restoreButton.GetComponent<Image>();
|
|
||||||
background.sprite = ButtonBackground.sprite;
|
|
||||||
|
|
||||||
Image restoreImage = restoreButton.transform.Find("X").GetComponent<Image>();
|
|
||||||
restoreImage.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(EItemAttributeId.EffectiveDist);
|
|
||||||
restoreImage.overrideSprite = null;
|
|
||||||
restoreImage.SetNativeSize();
|
|
||||||
restoreImage.transform.localScale = new Vector3(restoreImage.transform.localScale.x * 0.8f, restoreImage.transform.localScale.y * 0.8f, restoreImage.transform.localScale.z);
|
|
||||||
|
|
||||||
Image restoreImage2 = UnityEngine.Object.Instantiate(restoreImage, restoreImage.transform.parent, false);
|
|
||||||
restoreImage2.transform.Rotate(0f, 0f, 180f);
|
|
||||||
|
|
||||||
Vector3 startPosition = restoreImage2.transform.localPosition;
|
|
||||||
restoreImage.transform.localPosition = new Vector3(startPosition.x - 3f, startPosition.y - 3f, startPosition.z);
|
|
||||||
restoreImage2.transform.localPosition = new Vector3(startPosition.x + 2.5f, startPosition.y + 2f, startPosition.z);
|
|
||||||
|
|
||||||
if (SavedPreferredWidth < 0 && SavedPreferredHeight < 0)
|
|
||||||
{
|
|
||||||
restoreButton.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreButton.onClick.AddListener(() =>
|
|
||||||
{
|
|
||||||
SavedPreferredWidth = -1f;
|
|
||||||
SavedPreferredHeight = -1f;
|
|
||||||
RestoreSavedSize(inspectLayout);
|
|
||||||
restoreButton.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
// I'm really not sure why this is necessary, but something in the layout gets borked trying to just restore the default size
|
|
||||||
// This recreates a lot of the children, but it works
|
|
||||||
inspectPanel.method_1();
|
|
||||||
|
|
||||||
StretchDescription(inspectLayout);
|
|
||||||
});
|
|
||||||
inspectPanel.AddDisposable(() => restoreButton.onClick.RemoveAllListeners());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateLeftButton(ItemSpecificationPanel inspectPanel, Button template)
|
|
||||||
{
|
|
||||||
RectTransform templateRect = (RectTransform)template.transform;
|
|
||||||
|
|
||||||
Button leftButton = UnityEngine.Object.Instantiate(template, template.transform.parent, false);
|
|
||||||
leftButton.navigation = new Navigation() { mode = Navigation.Mode.None };
|
|
||||||
|
|
||||||
RectTransform leftRect = (RectTransform)leftButton.transform;
|
|
||||||
leftRect.localPosition = new Vector3(templateRect.localPosition.x - 2 * (templateRect.rect.width + ButtonPadding), templateRect.localPosition.y, templateRect.localPosition.z);
|
|
||||||
|
|
||||||
Image background = leftButton.GetComponent<Image>();
|
|
||||||
background.sprite = ButtonBackground.sprite;
|
|
||||||
|
|
||||||
Image leftImage = leftButton.transform.Find("X").GetComponent<Image>();
|
|
||||||
leftImage.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(EItemAttributeId.RecoilBack);
|
|
||||||
leftImage.overrideSprite = null;
|
|
||||||
leftImage.SetNativeSize();
|
|
||||||
|
|
||||||
leftButton.onClick.AddListener(() => SnapLeft(inspectPanel));
|
|
||||||
inspectPanel.AddDisposable(() => leftButton.onClick.RemoveAllListeners());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void CreateRightButton(ItemSpecificationPanel inspectPanel, Button template)
|
|
||||||
{
|
|
||||||
RectTransform templateRect = (RectTransform)template.transform;
|
|
||||||
|
|
||||||
Button rightButton = UnityEngine.Object.Instantiate(template, template.transform.parent, false);
|
|
||||||
rightButton.navigation = new Navigation() { mode = Navigation.Mode.None };
|
|
||||||
|
|
||||||
RectTransform rightRect = (RectTransform)rightButton.transform;
|
|
||||||
rightRect.localPosition = new Vector3(templateRect.localPosition.x - (templateRect.rect.width + ButtonPadding), templateRect.localPosition.y, templateRect.localPosition.z);
|
|
||||||
|
|
||||||
Image background = rightButton.GetComponent<Image>();
|
|
||||||
background.sprite = ButtonBackground.sprite;
|
|
||||||
|
|
||||||
Image rightImage = rightButton.transform.Find("X").GetComponent<Image>();
|
|
||||||
rightImage.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(EItemAttributeId.RecoilBack);
|
|
||||||
rightImage.transform.Rotate(0f, 180f, 0f);
|
|
||||||
rightImage.overrideSprite = null;
|
|
||||||
rightImage.SetNativeSize();
|
|
||||||
|
|
||||||
rightButton.onClick.AddListener(() => SnapRight(inspectPanel));
|
|
||||||
inspectPanel.AddDisposable(() => rightButton.onClick.RemoveAllListeners());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Informed by StretchArea.OnDrag
|
|
||||||
private static void RestoreSavedSize(LayoutElement layout)
|
|
||||||
{
|
|
||||||
RectTransform layoutRect = (RectTransform)layout.transform;
|
|
||||||
|
|
||||||
layout.preferredWidth = SavedPreferredWidth > 0 ? SavedPreferredWidth : DefaultPreferredWidth;
|
|
||||||
layout.preferredHeight = SavedPreferredHeight > 0 ? SavedPreferredHeight : DefaultPreferredHeight;
|
|
||||||
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(layoutRect);
|
|
||||||
|
|
||||||
layoutRect.CorrectPositionResolution(default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SnapLeft(ItemSpecificationPanel panel)
|
|
||||||
{
|
|
||||||
RectTransform inspectRect = (RectTransform)panel.transform;
|
|
||||||
inspectRect.anchoredPosition = new Vector2((float)Screen.width / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SnapRight(ItemSpecificationPanel panel)
|
|
||||||
{
|
|
||||||
RectTransform inspectRect = (RectTransform)panel.transform;
|
|
||||||
inspectRect.anchoredPosition = new Vector2((float)Screen.width * 3f / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StretchDescription(LayoutElement inspectLayout)
|
|
||||||
{
|
|
||||||
if (!Settings.ExpandDescriptionHeight.Value)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutElement scrollArea = inspectLayout.transform.Find("Inner/Contents/DescriptionPanel/DescriptionPanel/Scroll Area").GetComponent<LayoutElement>();
|
SavedPreferredWidth = ___layoutElement_0.preferredWidth;
|
||||||
if (inspectLayout != null && scrollArea != null && scrollArea.transform.childCount > 0)
|
SavedPreferredHeight = ___layoutElement_0.preferredHeight;
|
||||||
{
|
|
||||||
RectTransform description = (RectTransform)scrollArea.transform.GetChild(0);
|
|
||||||
|
|
||||||
// Try to figure out how much extra I can work with
|
Button resizeButton = ___layoutElement_0.transform.Find("Inner/Caption Panel/Restore")?.GetComponent<Button>();
|
||||||
float maxGrowth = (Screen.height / inspectLayout.transform.lossyScale.y) - ((RectTransform)inspectLayout.transform).rect.height;
|
if (resizeButton != null && !resizeButton.IsActive())
|
||||||
scrollArea.minHeight = Mathf.Max(scrollArea.minHeight, Mathf.Min(maxGrowth, description.rect.height));
|
{
|
||||||
|
resizeButton.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddInspectWindowButtonsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(LayoutElement ___layoutElement_0)
|
||||||
|
{
|
||||||
|
if (Settings.RememberInspectSize.Value)
|
||||||
|
{
|
||||||
|
RestoreSavedSize(___layoutElement_0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GrowInspectWindowDescriptionPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemSpecificationPanel __instance, LayoutElement ___layoutElement_0, ItemUiContext ___itemUiContext_0)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (Settings.LockInspectPreviewSize.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Inspect));
|
LayoutElement previewPanel = __instance.transform.Find("Inner/Contents/Preview Panel")?.GetComponent<LayoutElement>();
|
||||||
|
if (previewPanel != null)
|
||||||
|
{
|
||||||
|
previewPanel.flexibleHeight = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (ButtonBackground == null)
|
||||||
public static void Postfix(List<InputNode> ____children)
|
|
||||||
{
|
{
|
||||||
var inspectWindow = ____children.Last();
|
// Steal the background image fom gridwindow sort
|
||||||
if (inspectWindow != null)
|
ButtonBackground = ___itemUiContext_0.R().GridWindowTemplate.R().GridSortPanel.R().Button.image;
|
||||||
{
|
}
|
||||||
StretchDescription(inspectWindow.GetComponent<LayoutElement>());
|
|
||||||
}
|
Button closeButton = __instance.transform.Find("Inner/Caption Panel/Close Button")?.GetComponent<Button>();
|
||||||
|
if (closeButton != null)
|
||||||
|
{
|
||||||
|
CreateRightButton(__instance, closeButton);
|
||||||
|
CreateLeftButton(__instance, closeButton);
|
||||||
|
CreateRestoreButton(__instance, ___layoutElement_0, closeButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LeftRightKeybindsPatch : ModulePatch
|
private static void CreateRestoreButton(ItemSpecificationPanel inspectPanel, LayoutElement inspectLayout, Button template)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
RectTransform templateRect = (RectTransform)template.transform;
|
||||||
|
|
||||||
|
Button restoreButton = UnityEngine.Object.Instantiate(template, template.transform.parent, false);
|
||||||
|
restoreButton.name = "Restore";
|
||||||
|
restoreButton.navigation = new Navigation() { mode = Navigation.Mode.None };
|
||||||
|
|
||||||
|
RectTransform restoreRect = (RectTransform)restoreButton.transform;
|
||||||
|
restoreRect.localPosition = new Vector3(templateRect.localPosition.x - 3 * (templateRect.rect.width + ButtonPadding), templateRect.localPosition.y, templateRect.localPosition.z);
|
||||||
|
|
||||||
|
Image background = restoreButton.GetComponent<Image>();
|
||||||
|
background.sprite = ButtonBackground.sprite;
|
||||||
|
|
||||||
|
Image restoreImage = restoreButton.transform.Find("X").GetComponent<Image>();
|
||||||
|
restoreImage.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(EItemAttributeId.EffectiveDist);
|
||||||
|
restoreImage.overrideSprite = null;
|
||||||
|
restoreImage.SetNativeSize();
|
||||||
|
restoreImage.transform.localScale = new Vector3(restoreImage.transform.localScale.x * 0.8f, restoreImage.transform.localScale.y * 0.8f, restoreImage.transform.localScale.z);
|
||||||
|
|
||||||
|
Image restoreImage2 = UnityEngine.Object.Instantiate(restoreImage, restoreImage.transform.parent, false);
|
||||||
|
restoreImage2.transform.Rotate(0f, 0f, 180f);
|
||||||
|
|
||||||
|
Vector3 startPosition = restoreImage2.transform.localPosition;
|
||||||
|
restoreImage.transform.localPosition = new Vector3(startPosition.x - 3f, startPosition.y - 3f, startPosition.z);
|
||||||
|
restoreImage2.transform.localPosition = new Vector3(startPosition.x + 2.5f, startPosition.y + 2f, startPosition.z);
|
||||||
|
|
||||||
|
if (SavedPreferredWidth < 0 && SavedPreferredHeight < 0)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.Update));
|
restoreButton.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
restoreButton.onClick.AddListener(() =>
|
||||||
public static void Postfix(ItemSpecificationPanel __instance)
|
|
||||||
{
|
{
|
||||||
bool isTopWindow = __instance.transform.GetSiblingIndex() == __instance.transform.parent.childCount - 1;
|
SavedPreferredWidth = -1f;
|
||||||
if (Settings.SnapLeftKeybind.Value.IsDown() && isTopWindow)
|
SavedPreferredHeight = -1f;
|
||||||
{
|
RestoreSavedSize(inspectLayout);
|
||||||
SnapLeft(__instance);
|
restoreButton.gameObject.SetActive(false);
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings.SnapRightKeybind.Value.IsDown() && isTopWindow)
|
// I'm really not sure why this is necessary, but something in the layout gets borked trying to just restore the default size
|
||||||
{
|
// This recreates a lot of the children, but it works
|
||||||
SnapRight(__instance);
|
inspectPanel.method_1();
|
||||||
}
|
|
||||||
|
StretchDescription(inspectLayout);
|
||||||
|
});
|
||||||
|
inspectPanel.AddDisposable(() => restoreButton.onClick.RemoveAllListeners());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateLeftButton(ItemSpecificationPanel inspectPanel, Button template)
|
||||||
|
{
|
||||||
|
RectTransform templateRect = (RectTransform)template.transform;
|
||||||
|
|
||||||
|
Button leftButton = UnityEngine.Object.Instantiate(template, template.transform.parent, false);
|
||||||
|
leftButton.navigation = new Navigation() { mode = Navigation.Mode.None };
|
||||||
|
|
||||||
|
RectTransform leftRect = (RectTransform)leftButton.transform;
|
||||||
|
leftRect.localPosition = new Vector3(templateRect.localPosition.x - 2 * (templateRect.rect.width + ButtonPadding), templateRect.localPosition.y, templateRect.localPosition.z);
|
||||||
|
|
||||||
|
Image background = leftButton.GetComponent<Image>();
|
||||||
|
background.sprite = ButtonBackground.sprite;
|
||||||
|
|
||||||
|
Image leftImage = leftButton.transform.Find("X").GetComponent<Image>();
|
||||||
|
leftImage.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(EItemAttributeId.RecoilBack);
|
||||||
|
leftImage.overrideSprite = null;
|
||||||
|
leftImage.SetNativeSize();
|
||||||
|
|
||||||
|
leftButton.onClick.AddListener(() => SnapLeft(inspectPanel));
|
||||||
|
inspectPanel.AddDisposable(() => leftButton.onClick.RemoveAllListeners());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CreateRightButton(ItemSpecificationPanel inspectPanel, Button template)
|
||||||
|
{
|
||||||
|
RectTransform templateRect = (RectTransform)template.transform;
|
||||||
|
|
||||||
|
Button rightButton = UnityEngine.Object.Instantiate(template, template.transform.parent, false);
|
||||||
|
rightButton.navigation = new Navigation() { mode = Navigation.Mode.None };
|
||||||
|
|
||||||
|
RectTransform rightRect = (RectTransform)rightButton.transform;
|
||||||
|
rightRect.localPosition = new Vector3(templateRect.localPosition.x - (templateRect.rect.width + ButtonPadding), templateRect.localPosition.y, templateRect.localPosition.z);
|
||||||
|
|
||||||
|
Image background = rightButton.GetComponent<Image>();
|
||||||
|
background.sprite = ButtonBackground.sprite;
|
||||||
|
|
||||||
|
Image rightImage = rightButton.transform.Find("X").GetComponent<Image>();
|
||||||
|
rightImage.sprite = EFTHardSettings.Instance.StaticIcons.GetAttributeIcon(EItemAttributeId.RecoilBack);
|
||||||
|
rightImage.transform.Rotate(0f, 180f, 0f);
|
||||||
|
rightImage.overrideSprite = null;
|
||||||
|
rightImage.SetNativeSize();
|
||||||
|
|
||||||
|
rightButton.onClick.AddListener(() => SnapRight(inspectPanel));
|
||||||
|
inspectPanel.AddDisposable(() => rightButton.onClick.RemoveAllListeners());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Informed by StretchArea.OnDrag
|
||||||
|
private static void RestoreSavedSize(LayoutElement layout)
|
||||||
|
{
|
||||||
|
RectTransform layoutRect = (RectTransform)layout.transform;
|
||||||
|
|
||||||
|
layout.preferredWidth = SavedPreferredWidth > 0 ? SavedPreferredWidth : DefaultPreferredWidth;
|
||||||
|
layout.preferredHeight = SavedPreferredHeight > 0 ? SavedPreferredHeight : DefaultPreferredHeight;
|
||||||
|
|
||||||
|
LayoutRebuilder.ForceRebuildLayoutImmediate(layoutRect);
|
||||||
|
|
||||||
|
layoutRect.CorrectPositionResolution(default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SnapLeft(ItemSpecificationPanel panel)
|
||||||
|
{
|
||||||
|
RectTransform inspectRect = (RectTransform)panel.transform;
|
||||||
|
inspectRect.anchoredPosition = new Vector2((float)Screen.width / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SnapRight(ItemSpecificationPanel panel)
|
||||||
|
{
|
||||||
|
RectTransform inspectRect = (RectTransform)panel.transform;
|
||||||
|
inspectRect.anchoredPosition = new Vector2((float)Screen.width * 3f / 4f / inspectRect.lossyScale.x, inspectRect.anchoredPosition.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StretchDescription(LayoutElement inspectLayout)
|
||||||
|
{
|
||||||
|
if (!Settings.ExpandDescriptionHeight.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutElement scrollArea = inspectLayout.transform.Find("Inner/Contents/DescriptionPanel/DescriptionPanel/Scroll Area").GetComponent<LayoutElement>();
|
||||||
|
if (inspectLayout != null && scrollArea != null && scrollArea.transform.childCount > 0)
|
||||||
|
{
|
||||||
|
RectTransform description = (RectTransform)scrollArea.transform.GetChild(0);
|
||||||
|
|
||||||
|
// Try to figure out how much extra I can work with
|
||||||
|
float maxGrowth = (Screen.height / inspectLayout.transform.lossyScale.y) - ((RectTransform)inspectLayout.transform).rect.height;
|
||||||
|
scrollArea.minHeight = Mathf.Max(scrollArea.minHeight, Mathf.Min(maxGrowth, description.rect.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GrowInspectWindowDescriptionPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.Inspect));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(List<InputNode> ____children)
|
||||||
|
{
|
||||||
|
var inspectWindow = ____children.Last();
|
||||||
|
if (inspectWindow != null)
|
||||||
|
{
|
||||||
|
StretchDescription(inspectWindow.GetComponent<LayoutElement>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LeftRightKeybindsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.Update));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemSpecificationPanel __instance)
|
||||||
|
{
|
||||||
|
bool isTopWindow = __instance.transform.GetSiblingIndex() == __instance.transform.parent.childCount - 1;
|
||||||
|
if (Settings.SnapLeftKeybind.Value.IsDown() && isTopWindow)
|
||||||
|
{
|
||||||
|
SnapLeft(__instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings.SnapRightKeybind.Value.IsDown() && isTopWindow)
|
||||||
|
{
|
||||||
|
SnapRight(__instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,508 +13,507 @@ using TMPro;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class InspectWindowStatsPatches
|
||||||
{
|
{
|
||||||
public static class InspectWindowStatsPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new AddShowHideModStatsButtonPatch().Enable();
|
||||||
|
new CalculateModStatsPatch().Enable();
|
||||||
|
new CompareModStatsPatch().Enable();
|
||||||
|
new FormatCompactValuesPatch().Enable();
|
||||||
|
new FormatFullValuesPatch().Enable();
|
||||||
|
new FixDurabilityBarPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CalculateModStatsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new AddShowHideModStatsButtonPatch().Enable();
|
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_5));
|
||||||
new CalculateModStatsPatch().Enable();
|
|
||||||
new CompareModStatsPatch().Enable();
|
|
||||||
new FormatCompactValuesPatch().Enable();
|
|
||||||
new FormatFullValuesPatch().Enable();
|
|
||||||
new FixDurabilityBarPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CalculateModStatsPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(
|
||||||
|
ItemSpecificationPanel __instance,
|
||||||
|
Item ___item_0,
|
||||||
|
CompactCharacteristicPanel ____compactCharTemplate,
|
||||||
|
Transform ____compactPanel,
|
||||||
|
SimpleTooltip ___simpleTooltip_0)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var instance = __instance.R();
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
if (!Settings.ShowModStats.Value || ___item_0 is not Mod)
|
||||||
public static void Postfix(
|
|
||||||
ItemSpecificationPanel __instance,
|
|
||||||
Item ___item_0,
|
|
||||||
CompactCharacteristicPanel ____compactCharTemplate,
|
|
||||||
Transform ____compactPanel,
|
|
||||||
SimpleTooltip ___simpleTooltip_0)
|
|
||||||
{
|
|
||||||
var instance = __instance.R();
|
|
||||||
|
|
||||||
if (!Settings.ShowModStats.Value || ___item_0 is not Mod)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var deepAttributes = GetDeepAttributes(___item_0, out bool changed);
|
|
||||||
if (!changed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up existing one
|
|
||||||
if (instance.CompactCharacteristicPanels is IDisposable compactPanels)
|
|
||||||
{
|
|
||||||
compactPanels.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
var newCompactPanels = R.ItemSpecificationPanel.CreateCompactCharacteristicPanels(
|
|
||||||
deepAttributes,
|
|
||||||
____compactCharTemplate,
|
|
||||||
____compactPanel,
|
|
||||||
(attribute, viewer) => viewer.Show(attribute, ___simpleTooltip_0, __instance.Boolean_0, 100));
|
|
||||||
|
|
||||||
instance.CompactCharacteristicPanels = newCompactPanels;
|
|
||||||
|
|
||||||
if (newCompactPanels.Any())
|
|
||||||
{
|
|
||||||
newCompactPanels.Last().Value.OnTextWidthCalculated += __instance.method_3;
|
|
||||||
int siblingIndex = newCompactPanels.Last().Value.Transform.GetSiblingIndex();
|
|
||||||
|
|
||||||
foreach (var item in instance.CompactCharacteristicDropdowns)
|
|
||||||
{
|
|
||||||
item.Value.Transform.SetSiblingIndex(++siblingIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
__instance.method_10(0f);
|
|
||||||
__instance.method_6(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The fundamental thing about mods is that unlike weapons, armor, etc, they do not change their own attributes when they "accept" inner mods.
|
|
||||||
// I guess weapons figure out their stats by deeply iterating all mods, rather than just their direct mods
|
|
||||||
// As a result, the compare method that works with weapons/armor doesn't work with mods. Normally, it "adds" the mod, clones the result, then reverts the "add". Hence
|
|
||||||
// the compareItem is the item with the mods. But again, as mods don't change their values, we see no change.
|
|
||||||
// I wish I could prefix method_6 and update the compare item with the deep attributes, but that only works when adding a mod
|
|
||||||
// When removing, current item and compare item end up the same since current item never considers the mod anyway
|
|
||||||
// So I have to forcably call the refresh values method
|
|
||||||
public class CompareModStatsPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_6));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(Item compareItem)
|
|
||||||
{
|
|
||||||
if (compareItem == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Armor points is added in method_5, but not in other places so it's missed by compare
|
|
||||||
ArmorComponent[] armorComponents = compareItem.GetItemComponentsInChildren<ArmorComponent>(true).Where(c => c.ArmorClass > 0).ToArray<ArmorComponent>();
|
|
||||||
if (armorComponents.Any())
|
|
||||||
{
|
|
||||||
float maxDurability = armorComponents.Sum(c => c.Repairable.Durability);
|
|
||||||
|
|
||||||
var itemAttributeClass = new ItemAttributeClass(EItemAttributeId.ArmorPoints)
|
|
||||||
{
|
|
||||||
Name = EItemAttributeId.ArmorPoints.GetName(),
|
|
||||||
Base = () => maxDurability,
|
|
||||||
StringValue = () => Math.Round(maxDurability, 1).ToString(CultureInfo.InvariantCulture),
|
|
||||||
DisplayType = () => EItemAttributeDisplayType.Compact
|
|
||||||
};
|
|
||||||
|
|
||||||
compareItem.Attributes.Insert(0, itemAttributeClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ItemSpecificationPanel __instance, Item compareItem)
|
|
||||||
{
|
|
||||||
if (!Settings.ShowModStats.Value || compareItem is not Mod)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ItemAttributeClass> deepAttributes = GetDeepAttributes(compareItem, out bool changed);
|
|
||||||
if (!changed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var compactPanels = __instance.R().CompactCharacteristicPanels;
|
|
||||||
R.ItemSpecificationPanel.Refresh(compactPanels, deepAttributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddShowHideModStatsButtonPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_4));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetLabel()
|
|
||||||
{
|
|
||||||
return Settings.ShowModStats.Value ? "HIDE MOD STATS" : "SHOW MOD STATS";
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ItemSpecificationPanel __instance, ItemInfoInteractionsAbstractClass<EItemInfoButton> contextInteractions, Item ___item_0, InteractionButtonsContainer ____interactionButtonsContainer)
|
|
||||||
{
|
|
||||||
if (___item_0 is not Mod)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var buttonsContainer = ____interactionButtonsContainer.R();
|
|
||||||
|
|
||||||
ContextMenuButton toggleButton = null;
|
|
||||||
|
|
||||||
// Listen to the setting and the work there to handle multiple windows open at once
|
|
||||||
void onSettingChanged(object sender, EventArgs args)
|
|
||||||
{
|
|
||||||
var text = toggleButton.R().Text;
|
|
||||||
text.text = GetLabel();
|
|
||||||
|
|
||||||
__instance.method_5(); // rebuild stat panels
|
|
||||||
}
|
|
||||||
Settings.ShowModStats.SettingChanged += onSettingChanged;
|
|
||||||
|
|
||||||
static void onClick()
|
|
||||||
{
|
|
||||||
Settings.ShowModStats.Value = !Settings.ShowModStats.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createButton()
|
|
||||||
{
|
|
||||||
Sprite sprite = CacheResourcesPopAbstractClass.Pop<Sprite>("Characteristics/Icons/Modding");
|
|
||||||
toggleButton = (ContextMenuButton)UnityEngine.Object.Instantiate(buttonsContainer.ButtonTemplate, buttonsContainer.Container, false);
|
|
||||||
toggleButton.Show(GetLabel(), null, sprite, onClick, null);
|
|
||||||
____interactionButtonsContainer.method_5(toggleButton); // add to disposable list
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe to redraws to recreate when mods get dropped in
|
|
||||||
contextInteractions.OnRedrawRequired += createButton;
|
|
||||||
|
|
||||||
// And unsubscribe when the window goes away
|
|
||||||
buttonsContainer.UI.AddDisposable(() =>
|
|
||||||
{
|
|
||||||
contextInteractions.OnRedrawRequired -= createButton;
|
|
||||||
Settings.ShowModStats.SettingChanged -= onSettingChanged;
|
|
||||||
});
|
|
||||||
|
|
||||||
createButton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FormatCompactValuesPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(CompactCharacteristicPanel), nameof(CompactCharacteristicPanel.SetValues));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(CompactCharacteristicPanel __instance, TextMeshProUGUI ___ValueText)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FormatText(__instance, ___ValueText);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FormatFullValuesPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static MethodInfo RoundToIntMethod;
|
|
||||||
private static MethodInfo ToStringMethod;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
RoundToIntMethod = AccessTools.Method(typeof(Mathf), nameof(Mathf.RoundToInt));
|
|
||||||
ToStringMethod = AccessTools.Method(typeof(float), nameof(float.ToString), [typeof(string)]);
|
|
||||||
|
|
||||||
return AccessTools.Method(typeof(CharacteristicPanel), nameof(CharacteristicPanel.SetValues));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(CharacteristicPanel __instance, TextMeshProUGUI ___ValueText)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FormatText(__instance, ___ValueText, true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This transpiler looks for where it rounds a float to an int, and skips that. Instead it calls ToString("0.0#") on it
|
|
||||||
[PatchTranspiler]
|
|
||||||
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
int skip = 0;
|
|
||||||
CodeInstruction lastInstruction = null;
|
|
||||||
CodeInstruction currentInstruction = null;
|
|
||||||
foreach (var instruction in instructions)
|
|
||||||
{
|
|
||||||
if (lastInstruction == null)
|
|
||||||
{
|
|
||||||
lastInstruction = instruction;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentInstruction = instruction;
|
|
||||||
|
|
||||||
if (skip > 0)
|
|
||||||
{
|
|
||||||
--skip;
|
|
||||||
}
|
|
||||||
else if (currentInstruction.Calls(RoundToIntMethod))
|
|
||||||
{
|
|
||||||
yield return new CodeInstruction(OpCodes.Ldloca_S, 17);
|
|
||||||
yield return new CodeInstruction(OpCodes.Ldstr, "0.0#");
|
|
||||||
yield return new CodeInstruction(OpCodes.Call, ToStringMethod);
|
|
||||||
skip = 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
yield return lastInstruction;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastInstruction = instruction;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentInstruction != null)
|
|
||||||
{
|
|
||||||
yield return currentInstruction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FixDurabilityBarPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.DeclaredMethod(typeof(DurabilityPanel), nameof(DurabilityPanel.SetValues));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bar width is currently set to durability/100, and that 100 is pretty much hardcoded by the client
|
|
||||||
// Just clamp the bar to keep it from overflowing
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(Image ___Current)
|
|
||||||
{
|
|
||||||
___Current.rectTransform.anchorMax = new Vector2(
|
|
||||||
Mathf.Min(___Current.rectTransform.anchorMax.x, 1f),
|
|
||||||
___Current.rectTransform.anchorMax.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// These fields are percents, but have been manually multipied by 100 already
|
|
||||||
private static readonly EItemAttributeId[] NonPercentPercents = [EItemAttributeId.ChangeMovementSpeed, EItemAttributeId.ChangeTurningSpeed, EItemAttributeId.Ergonomics];
|
|
||||||
|
|
||||||
private static void FormatText(CompactCharacteristicPanel panel, TextMeshProUGUI textMesh, bool fullBar = false)
|
|
||||||
{
|
|
||||||
// Comparisons are shown as <value>(<changed>)
|
|
||||||
// <value> is from each attribute type's StringValue() function, so is formatted *mostly* ok
|
|
||||||
// <changed> is just naively formatted with ToString("F2"), so I have to figure out what it is and fix that
|
|
||||||
// This method is a gnarly pile of regex and replacements, blame BSG
|
|
||||||
if (!Settings.StyleItemPanel.Value)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These come from CompactCharacteristicPanel._increasingColor and _decreasingColor, which are hardcoded. Hardcoding here too because
|
var deepAttributes = GetDeepAttributes(___item_0, out bool changed);
|
||||||
// CharacteristicPanel doesn't define and you get clear
|
if (!changed)
|
||||||
const string IncreasingColorHex = "#5EC1FF";
|
|
||||||
const string DecreasingColorHex = "#C40000";
|
|
||||||
|
|
||||||
string text = textMesh.text;
|
|
||||||
var wrappedPanel = panel.R();
|
|
||||||
ItemAttributeClass attribute = wrappedPanel.ItemAttribute;
|
|
||||||
|
|
||||||
// Holy shit did they mess up MOA. Half of the calculation is done in the StringValue() method, so calculating delta from Base() loses all that
|
|
||||||
// Plus, they round the difference to the nearest integer (!?)
|
|
||||||
// Completely redo it
|
|
||||||
if ((EItemAttributeId)attribute.Id == EItemAttributeId.CenterOfImpact)
|
|
||||||
{
|
{
|
||||||
var compareAttribute = wrappedPanel.CompareItemAttribute;
|
return;
|
||||||
if (compareAttribute != null)
|
}
|
||||||
{
|
|
||||||
string currentStringValue = attribute.StringValue();
|
|
||||||
var moaMatch = Regex.Match(currentStringValue, @"^(\S+)");
|
|
||||||
if (float.TryParse(moaMatch.Groups[1].Value, out float moa))
|
|
||||||
{
|
|
||||||
string compareStringValue = compareAttribute.StringValue();
|
|
||||||
moaMatch = Regex.Match(compareStringValue, @"^(\S+)");
|
|
||||||
if (float.TryParse(moaMatch.Groups[1].Value, out float compareMoa))
|
|
||||||
{
|
|
||||||
float delta = compareMoa - moa;
|
|
||||||
string final = currentStringValue;
|
|
||||||
if (Math.Abs(delta) > 0)
|
|
||||||
{
|
|
||||||
string sign = delta > 0 ? "+" : "";
|
|
||||||
string color = (attribute.LessIsGood && delta < 0) || (!attribute.LessIsGood && delta > 0) ? IncreasingColorHex : DecreasingColorHex;
|
|
||||||
final += " <color=" + color + ">(" + sign + delta.ToString("0.0#") + ")</color>";
|
|
||||||
}
|
|
||||||
|
|
||||||
textMesh.text = final;
|
// Clean up existing one
|
||||||
return;
|
if (instance.CompactCharacteristicPanels is IDisposable compactPanels)
|
||||||
|
{
|
||||||
|
compactPanels.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
var newCompactPanels = R.ItemSpecificationPanel.CreateCompactCharacteristicPanels(
|
||||||
|
deepAttributes,
|
||||||
|
____compactCharTemplate,
|
||||||
|
____compactPanel,
|
||||||
|
(attribute, viewer) => viewer.Show(attribute, ___simpleTooltip_0, __instance.Boolean_0, 100));
|
||||||
|
|
||||||
|
instance.CompactCharacteristicPanels = newCompactPanels;
|
||||||
|
|
||||||
|
if (newCompactPanels.Any())
|
||||||
|
{
|
||||||
|
newCompactPanels.Last().Value.OnTextWidthCalculated += __instance.method_3;
|
||||||
|
int siblingIndex = newCompactPanels.Last().Value.Transform.GetSiblingIndex();
|
||||||
|
|
||||||
|
foreach (var item in instance.CompactCharacteristicDropdowns)
|
||||||
|
{
|
||||||
|
item.Value.Transform.SetSiblingIndex(++siblingIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__instance.method_10(0f);
|
||||||
|
__instance.method_6(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The fundamental thing about mods is that unlike weapons, armor, etc, they do not change their own attributes when they "accept" inner mods.
|
||||||
|
// I guess weapons figure out their stats by deeply iterating all mods, rather than just their direct mods
|
||||||
|
// As a result, the compare method that works with weapons/armor doesn't work with mods. Normally, it "adds" the mod, clones the result, then reverts the "add". Hence
|
||||||
|
// the compareItem is the item with the mods. But again, as mods don't change their values, we see no change.
|
||||||
|
// I wish I could prefix method_6 and update the compare item with the deep attributes, but that only works when adding a mod
|
||||||
|
// When removing, current item and compare item end up the same since current item never considers the mod anyway
|
||||||
|
// So I have to forcably call the refresh values method
|
||||||
|
public class CompareModStatsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_6));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(Item compareItem)
|
||||||
|
{
|
||||||
|
if (compareItem == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Armor points is added in method_5, but not in other places so it's missed by compare
|
||||||
|
ArmorComponent[] armorComponents = compareItem.GetItemComponentsInChildren<ArmorComponent>(true).Where(c => c.ArmorClass > 0).ToArray<ArmorComponent>();
|
||||||
|
if (armorComponents.Any())
|
||||||
|
{
|
||||||
|
float maxDurability = armorComponents.Sum(c => c.Repairable.Durability);
|
||||||
|
|
||||||
|
var itemAttributeClass = new ItemAttributeClass(EItemAttributeId.ArmorPoints)
|
||||||
|
{
|
||||||
|
Name = EItemAttributeId.ArmorPoints.GetName(),
|
||||||
|
Base = () => maxDurability,
|
||||||
|
StringValue = () => Math.Round(maxDurability, 1).ToString(CultureInfo.InvariantCulture),
|
||||||
|
DisplayType = () => EItemAttributeDisplayType.Compact
|
||||||
|
};
|
||||||
|
|
||||||
|
compareItem.Attributes.Insert(0, itemAttributeClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemSpecificationPanel __instance, Item compareItem)
|
||||||
|
{
|
||||||
|
if (!Settings.ShowModStats.Value || compareItem is not Mod)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ItemAttributeClass> deepAttributes = GetDeepAttributes(compareItem, out bool changed);
|
||||||
|
if (!changed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var compactPanels = __instance.R().CompactCharacteristicPanels;
|
||||||
|
R.ItemSpecificationPanel.Refresh(compactPanels, deepAttributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddShowHideModStatsButtonPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemSpecificationPanel), nameof(ItemSpecificationPanel.method_4));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetLabel()
|
||||||
|
{
|
||||||
|
return Settings.ShowModStats.Value ? "HIDE MOD STATS" : "SHOW MOD STATS";
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ItemSpecificationPanel __instance, ItemInfoInteractionsAbstractClass<EItemInfoButton> contextInteractions, Item ___item_0, InteractionButtonsContainer ____interactionButtonsContainer)
|
||||||
|
{
|
||||||
|
if (___item_0 is not Mod)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonsContainer = ____interactionButtonsContainer.R();
|
||||||
|
|
||||||
|
ContextMenuButton toggleButton = null;
|
||||||
|
|
||||||
|
// Listen to the setting and the work there to handle multiple windows open at once
|
||||||
|
void onSettingChanged(object sender, EventArgs args)
|
||||||
|
{
|
||||||
|
var text = toggleButton.R().Text;
|
||||||
|
text.text = GetLabel();
|
||||||
|
|
||||||
|
__instance.method_5(); // rebuild stat panels
|
||||||
|
}
|
||||||
|
Settings.ShowModStats.SettingChanged += onSettingChanged;
|
||||||
|
|
||||||
|
static void onClick()
|
||||||
|
{
|
||||||
|
Settings.ShowModStats.Value = !Settings.ShowModStats.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void createButton()
|
||||||
|
{
|
||||||
|
Sprite sprite = CacheResourcesPopAbstractClass.Pop<Sprite>("Characteristics/Icons/Modding");
|
||||||
|
toggleButton = (ContextMenuButton)UnityEngine.Object.Instantiate(buttonsContainer.ButtonTemplate, buttonsContainer.Container, false);
|
||||||
|
toggleButton.Show(GetLabel(), null, sprite, onClick, null);
|
||||||
|
____interactionButtonsContainer.method_5(toggleButton); // add to disposable list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to redraws to recreate when mods get dropped in
|
||||||
|
contextInteractions.OnRedrawRequired += createButton;
|
||||||
|
|
||||||
|
// And unsubscribe when the window goes away
|
||||||
|
buttonsContainer.UI.AddDisposable(() =>
|
||||||
|
{
|
||||||
|
contextInteractions.OnRedrawRequired -= createButton;
|
||||||
|
Settings.ShowModStats.SettingChanged -= onSettingChanged;
|
||||||
|
});
|
||||||
|
|
||||||
|
createButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FormatCompactValuesPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(CompactCharacteristicPanel), nameof(CompactCharacteristicPanel.SetValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(CompactCharacteristicPanel __instance, TextMeshProUGUI ___ValueText)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FormatText(__instance, ___ValueText);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FormatFullValuesPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static MethodInfo RoundToIntMethod;
|
||||||
|
private static MethodInfo ToStringMethod;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
RoundToIntMethod = AccessTools.Method(typeof(Mathf), nameof(Mathf.RoundToInt));
|
||||||
|
ToStringMethod = AccessTools.Method(typeof(float), nameof(float.ToString), [typeof(string)]);
|
||||||
|
|
||||||
|
return AccessTools.Method(typeof(CharacteristicPanel), nameof(CharacteristicPanel.SetValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(CharacteristicPanel __instance, TextMeshProUGUI ___ValueText)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FormatText(__instance, ___ValueText, true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This transpiler looks for where it rounds a float to an int, and skips that. Instead it calls ToString("0.0#") on it
|
||||||
|
[PatchTranspiler]
|
||||||
|
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
||||||
|
{
|
||||||
|
int skip = 0;
|
||||||
|
CodeInstruction lastInstruction = null;
|
||||||
|
CodeInstruction currentInstruction = null;
|
||||||
|
foreach (var instruction in instructions)
|
||||||
|
{
|
||||||
|
if (lastInstruction == null)
|
||||||
|
{
|
||||||
|
lastInstruction = instruction;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentInstruction = instruction;
|
||||||
|
|
||||||
|
if (skip > 0)
|
||||||
|
{
|
||||||
|
--skip;
|
||||||
|
}
|
||||||
|
else if (currentInstruction.Calls(RoundToIntMethod))
|
||||||
|
{
|
||||||
|
yield return new CodeInstruction(OpCodes.Ldloca_S, 17);
|
||||||
|
yield return new CodeInstruction(OpCodes.Ldstr, "0.0#");
|
||||||
|
yield return new CodeInstruction(OpCodes.Call, ToStringMethod);
|
||||||
|
skip = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yield return lastInstruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastInstruction = instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentInstruction != null)
|
||||||
|
{
|
||||||
|
yield return currentInstruction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FixDurabilityBarPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.DeclaredMethod(typeof(DurabilityPanel), nameof(DurabilityPanel.SetValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bar width is currently set to durability/100, and that 100 is pretty much hardcoded by the client
|
||||||
|
// Just clamp the bar to keep it from overflowing
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(Image ___Current)
|
||||||
|
{
|
||||||
|
___Current.rectTransform.anchorMax = new Vector2(
|
||||||
|
Mathf.Min(___Current.rectTransform.anchorMax.x, 1f),
|
||||||
|
___Current.rectTransform.anchorMax.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These fields are percents, but have been manually multipied by 100 already
|
||||||
|
private static readonly EItemAttributeId[] NonPercentPercents = [EItemAttributeId.ChangeMovementSpeed, EItemAttributeId.ChangeTurningSpeed, EItemAttributeId.Ergonomics];
|
||||||
|
|
||||||
|
private static void FormatText(CompactCharacteristicPanel panel, TextMeshProUGUI textMesh, bool fullBar = false)
|
||||||
|
{
|
||||||
|
// Comparisons are shown as <value>(<changed>)
|
||||||
|
// <value> is from each attribute type's StringValue() function, so is formatted *mostly* ok
|
||||||
|
// <changed> is just naively formatted with ToString("F2"), so I have to figure out what it is and fix that
|
||||||
|
// This method is a gnarly pile of regex and replacements, blame BSG
|
||||||
|
if (!Settings.StyleItemPanel.Value)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These come from CompactCharacteristicPanel._increasingColor and _decreasingColor, which are hardcoded. Hardcoding here too because
|
||||||
|
// CharacteristicPanel doesn't define and you get clear
|
||||||
|
const string IncreasingColorHex = "#5EC1FF";
|
||||||
|
const string DecreasingColorHex = "#C40000";
|
||||||
|
|
||||||
|
string text = textMesh.text;
|
||||||
|
var wrappedPanel = panel.R();
|
||||||
|
ItemAttributeClass attribute = wrappedPanel.ItemAttribute;
|
||||||
|
|
||||||
|
// Holy shit did they mess up MOA. Half of the calculation is done in the StringValue() method, so calculating delta from Base() loses all that
|
||||||
|
// Plus, they round the difference to the nearest integer (!?)
|
||||||
|
// Completely redo it
|
||||||
|
if ((EItemAttributeId)attribute.Id == EItemAttributeId.CenterOfImpact)
|
||||||
|
{
|
||||||
|
var compareAttribute = wrappedPanel.CompareItemAttribute;
|
||||||
|
if (compareAttribute != null)
|
||||||
|
{
|
||||||
|
string currentStringValue = attribute.StringValue();
|
||||||
|
var moaMatch = Regex.Match(currentStringValue, @"^(\S+)");
|
||||||
|
if (float.TryParse(moaMatch.Groups[1].Value, out float moa))
|
||||||
|
{
|
||||||
|
string compareStringValue = compareAttribute.StringValue();
|
||||||
|
moaMatch = Regex.Match(compareStringValue, @"^(\S+)");
|
||||||
|
if (float.TryParse(moaMatch.Groups[1].Value, out float compareMoa))
|
||||||
|
{
|
||||||
|
float delta = compareMoa - moa;
|
||||||
|
string final = currentStringValue;
|
||||||
|
if (Math.Abs(delta) > 0)
|
||||||
|
{
|
||||||
|
string sign = delta > 0 ? "+" : "";
|
||||||
|
string color = (attribute.LessIsGood && delta < 0) || (!attribute.LessIsGood && delta > 0) ? IncreasingColorHex : DecreasingColorHex;
|
||||||
|
final += " <color=" + color + ">(" + sign + delta.ToString("0.0#") + ")</color>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
textMesh.text = final;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Some percents are formatted with ToString("P1"), which puts a space before the %. These are percents from 0-1, so the <changed> value need to be converted
|
// Some percents are formatted with ToString("P1"), which puts a space before the %. These are percents from 0-1, so the <changed> value need to be converted
|
||||||
var match = Regex.Match(text, @" %\(([+-].*)\)");
|
var match = Regex.Match(text, @" %\(([+-].*)\)");
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
// If this fails to parse, I don't know what it is, leave it be
|
||||||
|
if (float.TryParse(match.Groups[1].Value, out float value))
|
||||||
|
{
|
||||||
|
string sign = value > 0 ? "+" : "";
|
||||||
|
string color = (attribute.LessIsGood && value < 0) || (!attribute.LessIsGood && value > 0) ? IncreasingColorHex : DecreasingColorHex;
|
||||||
|
|
||||||
|
// Except some that have a space weren't actually formatted with P1 and are 0-100 with a manually added " %"
|
||||||
|
if (NonPercentPercents.Contains((EItemAttributeId)attribute.Id))
|
||||||
|
{
|
||||||
|
text = Regex.Replace(text, @"%\([+-].*\)", "%<color=" + color + ">(" + sign + value + "%)</color>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = Regex.Replace(text, @"%\([+-].*\)", "%<color=" + color + ">(" + sign + value.ToString("P1") + ")</color>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Others are rendered as num + "%", so there's no space before the %. These are percents but are from 0-100, not 0-1.
|
||||||
|
match = Regex.Match(text, @"(\S)%\(([+-].*)\)");
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
// If this fails to parse, I don't know what it is, leave it be
|
// If this fails to parse, I don't know what it is, leave it be
|
||||||
if (float.TryParse(match.Groups[1].Value, out float value))
|
if (float.TryParse(match.Groups[2].Value, out float value))
|
||||||
{
|
{
|
||||||
string sign = value > 0 ? "+" : "";
|
string sign = value > 0 ? "+" : "";
|
||||||
string color = (attribute.LessIsGood && value < 0) || (!attribute.LessIsGood && value > 0) ? IncreasingColorHex : DecreasingColorHex;
|
string color = (attribute.LessIsGood && value < 0) || (!attribute.LessIsGood && value > 0) ? IncreasingColorHex : DecreasingColorHex;
|
||||||
|
text = Regex.Replace(text, @"(\S)%\(([+-].*)\)", match.Groups[1].Value + "%<color=" + color + ">(" + sign + value + "%)</color>");
|
||||||
// Except some that have a space weren't actually formatted with P1 and are 0-100 with a manually added " %"
|
|
||||||
if (NonPercentPercents.Contains((EItemAttributeId)attribute.Id))
|
|
||||||
{
|
|
||||||
text = Regex.Replace(text, @"%\([+-].*\)", "%<color=" + color + ">(" + sign + value + "%)</color>");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
text = Regex.Replace(text, @"%\([+-].*\)", "%<color=" + color + ">(" + sign + value.ToString("P1") + ")</color>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Others are rendered as num + "%", so there's no space before the %. These are percents but are from 0-100, not 0-1.
|
// Finally the ones that aren't percents
|
||||||
match = Regex.Match(text, @"(\S)%\(([+-].*)\)");
|
match = Regex.Match(text, @"\(([+-].*)\)");
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
// If this fails to parse, I don't know what it is, leave it be
|
// If this fails to parse, I don't know what it is, leave it be
|
||||||
if (float.TryParse(match.Groups[2].Value, out float value))
|
if (float.TryParse(match.Groups[1].Value, out float value))
|
||||||
{
|
{
|
||||||
string sign = value > 0 ? "+" : "";
|
string sign = value > 0 ? "+" : "";
|
||||||
string color = (attribute.LessIsGood && value < 0) || (!attribute.LessIsGood && value > 0) ? IncreasingColorHex : DecreasingColorHex;
|
string color = (attribute.LessIsGood && value < 0) || (!attribute.LessIsGood && value > 0) ? IncreasingColorHex : DecreasingColorHex;
|
||||||
text = Regex.Replace(text, @"(\S)%\(([+-].*)\)", match.Groups[1].Value + "%<color=" + color + ">(" + sign + value + "%)</color>");
|
if (fullBar && Math.Abs(value) >= 1)
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Finally the ones that aren't percents
|
|
||||||
match = Regex.Match(text, @"\(([+-].*)\)");
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
// If this fails to parse, I don't know what it is, leave it be
|
|
||||||
if (float.TryParse(match.Groups[1].Value, out float value))
|
|
||||||
{
|
{
|
||||||
string sign = value > 0 ? "+" : "";
|
// Fullbar rounds to nearest int, but I transpiled it not to. Restore the rounding, but only if the value won't just round to 0
|
||||||
string color = (attribute.LessIsGood && value < 0) || (!attribute.LessIsGood && value > 0) ? IncreasingColorHex : DecreasingColorHex;
|
value = Mathf.RoundToInt(value);
|
||||||
if (fullBar && Math.Abs(value) >= 1)
|
|
||||||
{
|
|
||||||
// Fullbar rounds to nearest int, but I transpiled it not to. Restore the rounding, but only if the value won't just round to 0
|
|
||||||
value = Mathf.RoundToInt(value);
|
|
||||||
}
|
|
||||||
text = Regex.Replace(text, @"\(([+-].*)\)", "<color=" + color + ">(" + sign + value + ")</color>");
|
|
||||||
}
|
}
|
||||||
|
text = Regex.Replace(text, @"\(([+-].*)\)", "<color=" + color + ">(" + sign + value + ")</color>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove trailing 0s
|
|
||||||
text = RemoveTrailingZeros(text);
|
|
||||||
|
|
||||||
// Fix spacing
|
|
||||||
text = text.Replace(" %", "%");
|
|
||||||
text = text.Replace("(", " (");
|
|
||||||
|
|
||||||
textMesh.text = text;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ItemAttributeClass> GetDeepAttributes(Item item, out bool changed)
|
// Remove trailing 0s
|
||||||
{
|
text = RemoveTrailingZeros(text);
|
||||||
changed = false;
|
|
||||||
List<ItemAttributeClass> itemAttributes = item.Attributes.Where(a => a.DisplayType() == EItemAttributeDisplayType.Compact).ToList();
|
|
||||||
foreach (var subItem in item.GetAllItems()) // This get all items, recursively
|
|
||||||
{
|
|
||||||
if (subItem == item)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var subAttributes = subItem.Attributes.Where(a => a.DisplayType() == EItemAttributeDisplayType.Compact).ToList();
|
// Fix spacing
|
||||||
itemAttributes = CombineAttributes(itemAttributes, subAttributes).ToList();
|
text = text.Replace(" %", "%");
|
||||||
changed = true;
|
text = text.Replace("(", " (");
|
||||||
|
|
||||||
|
textMesh.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<ItemAttributeClass> GetDeepAttributes(Item item, out bool changed)
|
||||||
|
{
|
||||||
|
changed = false;
|
||||||
|
List<ItemAttributeClass> itemAttributes = item.Attributes.Where(a => a.DisplayType() == EItemAttributeDisplayType.Compact).ToList();
|
||||||
|
foreach (var subItem in item.GetAllItems()) // This get all items, recursively
|
||||||
|
{
|
||||||
|
if (subItem == item)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return itemAttributes;
|
var subAttributes = subItem.Attributes.Where(a => a.DisplayType() == EItemAttributeDisplayType.Compact).ToList();
|
||||||
|
itemAttributes = CombineAttributes(itemAttributes, subAttributes).ToList();
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<ItemAttributeClass> CombineAttributes(IList<ItemAttributeClass> first, IList<ItemAttributeClass> second)
|
return itemAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<ItemAttributeClass> CombineAttributes(IList<ItemAttributeClass> first, IList<ItemAttributeClass> second)
|
||||||
|
{
|
||||||
|
foreach (EItemAttributeId id in first.Select(a => a.Id).Union(second.Select(a => a.Id)).Select(v => (EItemAttributeId)v))
|
||||||
{
|
{
|
||||||
foreach (EItemAttributeId id in first.Select(a => a.Id).Union(second.Select(a => a.Id)).Select(v => (EItemAttributeId)v))
|
// Need to cast the id since it's of type Enum for some reason
|
||||||
|
var attribute = first.FirstOrDefault(a => (EItemAttributeId)a.Id == id);
|
||||||
|
var other = second.FirstOrDefault(a => (EItemAttributeId)a.Id == id);
|
||||||
|
if (attribute == null)
|
||||||
{
|
{
|
||||||
// Need to cast the id since it's of type Enum for some reason
|
yield return other;
|
||||||
var attribute = first.FirstOrDefault(a => (EItemAttributeId)a.Id == id);
|
|
||||||
var other = second.FirstOrDefault(a => (EItemAttributeId)a.Id == id);
|
|
||||||
if (attribute == null)
|
|
||||||
{
|
|
||||||
yield return other;
|
|
||||||
}
|
|
||||||
else if (other == null)
|
|
||||||
{
|
|
||||||
yield return attribute;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var combined = attribute.Clone();
|
|
||||||
switch (attribute.Id)
|
|
||||||
{
|
|
||||||
case EItemAttributeId.EffectiveDist:
|
|
||||||
case EItemAttributeId.SightingRange:
|
|
||||||
combined.Base = () => Math.Max(attribute.Base(), other.Base());
|
|
||||||
combined.StringValue = () => combined.Base().ToString();
|
|
||||||
break;
|
|
||||||
case EItemAttributeId.Accuracy:
|
|
||||||
case EItemAttributeId.Recoil:
|
|
||||||
combined.Base = () => attribute.Base() + other.Base();
|
|
||||||
combined.StringValue = () => combined.Base() + "%";
|
|
||||||
break;
|
|
||||||
case EItemAttributeId.Loudness:
|
|
||||||
case EItemAttributeId.Ergonomics:
|
|
||||||
case EItemAttributeId.Velocity:
|
|
||||||
combined.Base = () => attribute.Base() + other.Base();
|
|
||||||
combined.StringValue = () => combined.Base().ToString();
|
|
||||||
break;
|
|
||||||
case EItemAttributeId.DurabilityBurn:
|
|
||||||
case EItemAttributeId.HeatFactor:
|
|
||||||
case EItemAttributeId.CoolFactor:
|
|
||||||
combined.Base = () => attribute.Base() + other.Base();
|
|
||||||
combined.StringValue = () => combined.Base().ToString("P1");
|
|
||||||
break;
|
|
||||||
case EItemAttributeId.RaidModdable:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield return combined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else if (other == null)
|
||||||
|
{
|
||||||
|
yield return attribute;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var combined = attribute.Clone();
|
||||||
|
switch (attribute.Id)
|
||||||
|
{
|
||||||
|
case EItemAttributeId.EffectiveDist:
|
||||||
|
case EItemAttributeId.SightingRange:
|
||||||
|
combined.Base = () => Math.Max(attribute.Base(), other.Base());
|
||||||
|
combined.StringValue = () => combined.Base().ToString();
|
||||||
|
break;
|
||||||
|
case EItemAttributeId.Accuracy:
|
||||||
|
case EItemAttributeId.Recoil:
|
||||||
|
combined.Base = () => attribute.Base() + other.Base();
|
||||||
|
combined.StringValue = () => combined.Base() + "%";
|
||||||
|
break;
|
||||||
|
case EItemAttributeId.Loudness:
|
||||||
|
case EItemAttributeId.Ergonomics:
|
||||||
|
case EItemAttributeId.Velocity:
|
||||||
|
combined.Base = () => attribute.Base() + other.Base();
|
||||||
|
combined.StringValue = () => combined.Base().ToString();
|
||||||
|
break;
|
||||||
|
case EItemAttributeId.DurabilityBurn:
|
||||||
|
case EItemAttributeId.HeatFactor:
|
||||||
|
case EItemAttributeId.CoolFactor:
|
||||||
|
combined.Base = () => attribute.Base() + other.Base();
|
||||||
|
combined.StringValue = () => combined.Base().ToString("P1");
|
||||||
|
break;
|
||||||
|
case EItemAttributeId.RaidModdable:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
public static string RemoveTrailingZeros(string input)
|
yield return combined;
|
||||||
{
|
}
|
||||||
// This matches: a number (so it doesn't apply to periods in words), named "integer"
|
|
||||||
// Followed by either
|
|
||||||
// a) a dot, some digits, and then a non-zero digit (named "significantDigits"), which is followed by one or more trailing 0
|
|
||||||
// b) a dot and some trailing 0
|
|
||||||
// And all that is replaced to the original integer, and the significantDigits (if they exist)
|
|
||||||
// If neither matches this doesn't match and does nothing
|
|
||||||
return Regex.Replace(input, @"(?<integer>\d)((?<significantDecimals>\.[0-9]*[1-9])0*\b)?(\.0+\b)?", "${integer}${significantDecimals}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string RemoveTrailingZeros(string input)
|
||||||
|
{
|
||||||
|
// This matches: a number (so it doesn't apply to periods in words), named "integer"
|
||||||
|
// Followed by either
|
||||||
|
// a) a dot, some digits, and then a non-zero digit (named "significantDigits"), which is followed by one or more trailing 0
|
||||||
|
// b) a dot and some trailing 0
|
||||||
|
// And all that is replaced to the original integer, and the significantDigits (if they exist)
|
||||||
|
// If neither matches this doesn't match and does nothing
|
||||||
|
return Regex.Replace(input, @"(?<integer>\d)((?<significantDecimals>\.[0-9]*[1-9])0*\b)?(\.0+\b)?", "${integer}${significantDecimals}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,50 +5,49 @@ using HarmonyLib;
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class KeepMessagesOpenPatches
|
||||||
{
|
{
|
||||||
public static class KeepMessagesOpenPatches
|
private static bool ReopenMessages = false;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static bool ReopenMessages = false;
|
new SniffChatPanelClosePatch().Enable();
|
||||||
|
new ReopenMessagesPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
public class SniffChatPanelClosePatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new SniffChatPanelClosePatch().Enable();
|
return AccessTools.Method(typeof(ChatScreen), nameof(ChatScreen.method_6));
|
||||||
new ReopenMessagesPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SniffChatPanelClosePatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (Settings.KeepMessagesOpen.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ChatScreen), nameof(ChatScreen.method_6));
|
ReopenMessages = true;
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
if (Settings.KeepMessagesOpen.Value)
|
|
||||||
{
|
|
||||||
ReopenMessages = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class ReopenMessagesPatch : ModulePatch
|
public class ReopenMessagesPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_0));
|
||||||
{
|
}
|
||||||
return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_0));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
[PatchPostfix]
|
||||||
public static void Postfix(MainMenuController __instance, EEftScreenType eftScreenType)
|
public static void Postfix(MainMenuController __instance, EEftScreenType eftScreenType)
|
||||||
|
{
|
||||||
|
if (Settings.KeepMessagesOpen.Value && eftScreenType != EEftScreenType.TransferItems && ReopenMessages)
|
||||||
{
|
{
|
||||||
if (Settings.KeepMessagesOpen.Value && eftScreenType != EEftScreenType.TransferItems && ReopenMessages)
|
ReopenMessages = false;
|
||||||
{
|
__instance.ShowScreen(EMenuType.Chat, true);
|
||||||
ReopenMessages = false;
|
|
||||||
__instance.ShowScreen(EMenuType.Chat, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,153 +7,152 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class KeepOfferWindowOpenPatches
|
||||||
{
|
{
|
||||||
public static class KeepOfferWindowOpenPatches
|
private static bool BlockClose = false;
|
||||||
|
|
||||||
|
private static TaskCompletionSource CompletionSource;
|
||||||
|
private static readonly List<Task> AddOfferTasks = [];
|
||||||
|
private static AddOfferWindow Window;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static bool BlockClose = false;
|
new GetTaskCompletionSourcePatch().Enable();
|
||||||
|
new PlaceOfferClickPatch().Enable();
|
||||||
|
new ClosePatch().Enable();
|
||||||
|
new ManageTaskPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
private static TaskCompletionSource CompletionSource;
|
public class GetTaskCompletionSourcePatch : ModulePatch
|
||||||
private static readonly List<Task> AddOfferTasks = [];
|
{
|
||||||
private static AddOfferWindow Window;
|
protected override MethodBase GetTargetMethod()
|
||||||
|
|
||||||
public static void Enable()
|
|
||||||
{
|
{
|
||||||
new GetTaskCompletionSourcePatch().Enable();
|
return AccessTools.DeclaredMethod(typeof(AddOfferWindow), nameof(AddOfferWindow.Show));
|
||||||
new PlaceOfferClickPatch().Enable();
|
|
||||||
new ClosePatch().Enable();
|
|
||||||
new ManageTaskPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GetTaskCompletionSourcePatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(AddOfferWindow __instance, ref Task __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.KeepAddOfferOpen.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.DeclaredMethod(typeof(AddOfferWindow), nameof(AddOfferWindow.Show));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
Window = __instance;
|
||||||
public static void Postfix(AddOfferWindow __instance, ref Task __result)
|
AddOfferTasks.Clear();
|
||||||
|
|
||||||
|
// Use a different task to mark when everything is actually done
|
||||||
|
CompletionSource = new TaskCompletionSource();
|
||||||
|
__result = CompletionSource.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlaceOfferClickPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(AddOfferWindow __instance, TaskCompletionSource ___taskCompletionSource_0, ref TaskCompletionSource __state)
|
||||||
|
{
|
||||||
|
if (!Settings.KeepAddOfferOpen.Value)
|
||||||
{
|
{
|
||||||
if (!Settings.KeepAddOfferOpen.Value)
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Window = __instance;
|
__state = ___taskCompletionSource_0;
|
||||||
AddOfferTasks.Clear();
|
|
||||||
|
|
||||||
// Use a different task to mark when everything is actually done
|
// Close the window if you're gonna hit max offers
|
||||||
CompletionSource = new TaskCompletionSource();
|
var ragfair = __instance.R().Ragfair;
|
||||||
__result = CompletionSource.Task;
|
if (Settings.KeepAddOfferOpenIgnoreMaxOffers.Value || ragfair.MyOffersCount + 1 < ragfair.GetMaxOffersCount(ragfair.MyRating))
|
||||||
|
{
|
||||||
|
BlockClose = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlaceOfferClickPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(RequirementView[] ____requirementViews, TaskCompletionSource ___taskCompletionSource_0, ref TaskCompletionSource __state)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
BlockClose = false;
|
||||||
|
|
||||||
|
if (!Settings.KeepAddOfferOpen.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.method_1));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
// If the taskCompletionSource member was changed, then it's adding an offer :S
|
||||||
public static void Prefix(AddOfferWindow __instance, TaskCompletionSource ___taskCompletionSource_0, ref TaskCompletionSource __state)
|
if (__state != ___taskCompletionSource_0)
|
||||||
{
|
{
|
||||||
if (!Settings.KeepAddOfferOpen.Value)
|
AddOfferTasks.Add(__state.Task); // This is the task completion source passed into the add offer call
|
||||||
|
|
||||||
|
// clear old prices
|
||||||
|
foreach (var requirementView in ____requirementViews)
|
||||||
{
|
{
|
||||||
return;
|
requirementView.ResetRequirementInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
__state = ___taskCompletionSource_0;
|
|
||||||
|
|
||||||
// Close the window if you're gonna hit max offers
|
|
||||||
var ragfair = __instance.R().Ragfair;
|
|
||||||
if (Settings.KeepAddOfferOpenIgnoreMaxOffers.Value || ragfair.MyOffersCount + 1 < ragfair.GetMaxOffersCount(ragfair.MyRating))
|
|
||||||
{
|
|
||||||
BlockClose = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(RequirementView[] ____requirementViews, TaskCompletionSource ___taskCompletionSource_0, ref TaskCompletionSource __state)
|
|
||||||
{
|
|
||||||
BlockClose = false;
|
|
||||||
|
|
||||||
if (!Settings.KeepAddOfferOpen.Value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the taskCompletionSource member was changed, then it's adding an offer :S
|
|
||||||
if (__state != ___taskCompletionSource_0)
|
|
||||||
{
|
|
||||||
AddOfferTasks.Add(__state.Task); // This is the task completion source passed into the add offer call
|
|
||||||
|
|
||||||
// clear old prices
|
|
||||||
foreach (var requirementView in ____requirementViews)
|
|
||||||
{
|
|
||||||
requirementView.ResetRequirementInformation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ClosePatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(Window.Close));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix()
|
|
||||||
{
|
|
||||||
if (!Settings.KeepAddOfferOpen.Value)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BlockClose && CompletionSource != null && AddOfferTasks.All(t => t.IsCompleted))
|
|
||||||
{
|
|
||||||
CompletionSource.Complete();
|
|
||||||
CompletionSource = null;
|
|
||||||
AddOfferTasks.Clear();
|
|
||||||
Window = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !BlockClose;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The window has a task completion source that completes when closing window or upon successful offer placement (which assumes window closes too)
|
|
||||||
// Replace implementation to ensure it only completes when window is closed, or placement is successful AND window has since closed
|
|
||||||
public class ManageTaskPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
Type type = typeof(AddOfferWindow).GetNestedTypes().Single(t => t.GetField("completionSource") != null); // AddOfferWindow.Class3068
|
|
||||||
return AccessTools.Method(type, "method_0");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(TaskCompletionSource ___completionSource)
|
|
||||||
{
|
|
||||||
if (!Settings.KeepAddOfferOpen.Value || Window == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
___completionSource.Complete();
|
|
||||||
if (!Window.gameObject.activeInHierarchy && AddOfferTasks.All(t => t.IsCompleted))
|
|
||||||
{
|
|
||||||
CompletionSource.Complete();
|
|
||||||
CompletionSource = null;
|
|
||||||
AddOfferTasks.Clear();
|
|
||||||
Window = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ClosePatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(Window.Close));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix()
|
||||||
|
{
|
||||||
|
if (!Settings.KeepAddOfferOpen.Value)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BlockClose && CompletionSource != null && AddOfferTasks.All(t => t.IsCompleted))
|
||||||
|
{
|
||||||
|
CompletionSource.Complete();
|
||||||
|
CompletionSource = null;
|
||||||
|
AddOfferTasks.Clear();
|
||||||
|
Window = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !BlockClose;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The window has a task completion source that completes when closing window or upon successful offer placement (which assumes window closes too)
|
||||||
|
// Replace implementation to ensure it only completes when window is closed, or placement is successful AND window has since closed
|
||||||
|
public class ManageTaskPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
Type type = typeof(AddOfferWindow).GetNestedTypes().Single(t => t.GetField("completionSource") != null); // AddOfferWindow.Class3068
|
||||||
|
return AccessTools.Method(type, "method_0");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(TaskCompletionSource ___completionSource)
|
||||||
|
{
|
||||||
|
if (!Settings.KeepAddOfferOpen.Value || Window == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
___completionSource.Complete();
|
||||||
|
if (!Window.gameObject.activeInHierarchy && AddOfferTasks.All(t => t.IsCompleted))
|
||||||
|
{
|
||||||
|
CompletionSource.Complete();
|
||||||
|
CompletionSource = null;
|
||||||
|
AddOfferTasks.Clear();
|
||||||
|
Window = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,36 +6,35 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class KeepWindowsOnScreenPatches
|
||||||
{
|
{
|
||||||
public static class KeepWindowsOnScreenPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new KeepWindowOnScreenPatch(nameof(ItemUiContext.Inspect)).Enable();
|
||||||
|
new KeepWindowOnScreenPatch(nameof(ItemUiContext.EditTag)).Enable();
|
||||||
|
new KeepWindowOnScreenPatch(nameof(ItemUiContext.OpenInsuranceWindow)).Enable();
|
||||||
|
new KeepWindowOnScreenPatch(nameof(ItemUiContext.OpenRepairWindow)).Enable();
|
||||||
|
new KeepWindowOnScreenPatch(nameof(ItemUiContext.method_2)).Enable(); // grids
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FixNewestWindow(List<InputNode> windows)
|
||||||
|
{
|
||||||
|
UIInputNode newWindow = windows.Last() as UIInputNode;
|
||||||
|
newWindow?.CorrectPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KeepWindowOnScreenPatch(string methodName) : ModulePatch
|
||||||
|
{
|
||||||
|
private readonly string methodName = methodName;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new KeepWindowOnScreenPatch(nameof(ItemUiContext.Inspect)).Enable();
|
return AccessTools.Method(typeof(ItemUiContext), methodName);
|
||||||
new KeepWindowOnScreenPatch(nameof(ItemUiContext.EditTag)).Enable();
|
|
||||||
new KeepWindowOnScreenPatch(nameof(ItemUiContext.OpenInsuranceWindow)).Enable();
|
|
||||||
new KeepWindowOnScreenPatch(nameof(ItemUiContext.OpenRepairWindow)).Enable();
|
|
||||||
new KeepWindowOnScreenPatch(nameof(ItemUiContext.method_2)).Enable(); // grids
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void FixNewestWindow(List<InputNode> windows)
|
[PatchPostfix]
|
||||||
{
|
public static void Postfix(List<InputNode> ____children) => FixNewestWindow(____children);
|
||||||
UIInputNode newWindow = windows.Last() as UIInputNode;
|
|
||||||
newWindow?.CorrectPosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KeepWindowOnScreenPatch(string methodName) : ModulePatch
|
|
||||||
{
|
|
||||||
private readonly string methodName = methodName;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ItemUiContext), methodName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(List<InputNode> ____children) => FixNewestWindow(____children);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,76 +8,75 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class LoadAmmoInRaidPatches
|
||||||
{
|
{
|
||||||
public class LoadAmmoInRaidPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new EnableContextMenuPatch().Enable();
|
||||||
|
new SlowLoadingPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EnableContextMenuPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new EnableContextMenuPatch().Enable();
|
return AccessTools.Method(R.ContextMenuHelper.Type, "IsActive");
|
||||||
new SlowLoadingPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EnableContextMenuPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(EItemInfoButton button, ref bool __result, Item ___item_0)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (button != EItemInfoButton.LoadAmmo || !Plugin.InRaid() || !Settings.EnableLoadAmmo.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(R.ContextMenuHelper.Type, "IsActive");
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
__result = MagazineBuildClass.TryFindPresetSource(___item_0).Succeeded;
|
||||||
public static bool Prefix(EItemInfoButton button, ref bool __result, Item ___item_0)
|
return false;
|
||||||
{
|
}
|
||||||
if (button != EItemInfoButton.LoadAmmo || !Plugin.InRaid() || !Settings.EnableLoadAmmo.Value)
|
}
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
__result = MagazineBuildClass.TryFindPresetSource(___item_0).Succeeded;
|
public class SlowLoadingPatch : ModulePatch
|
||||||
return false;
|
{
|
||||||
}
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.LoadAmmoByType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SlowLoadingPatch : ModulePatch
|
// This code is a mix of ItemUiContext.LoadAmmoByType, but then switching over to GridView.AcceptItem
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(ItemUiContext __instance, MagazineClass magazine, string ammoTemplateId, ref Task __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Plugin.InRaid() || !Settings.EnableLoadAmmo.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.LoadAmmoByType));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This code is a mix of ItemUiContext.LoadAmmoByType, but then switching over to GridView.AcceptItem
|
InventoryControllerClass inventoryController = __instance.R().InventoryController;
|
||||||
[PatchPrefix]
|
EquipmentClass equipment = inventoryController.Inventory.Equipment;
|
||||||
public static bool Prefix(ItemUiContext __instance, MagazineClass magazine, string ammoTemplateId, ref Task __result)
|
|
||||||
|
List<BulletClass> ammo = [];
|
||||||
|
equipment.GetAllAssembledItems(ammo);
|
||||||
|
|
||||||
|
// Just do the first stack
|
||||||
|
BulletClass bullets = ammo.Where(a => a.TemplateId == ammoTemplateId && a.Parent.Container is not Slot)
|
||||||
|
.OrderBy(a => a.SpawnedInSession)
|
||||||
|
.ThenBy(a => a.StackObjectsCount)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (bullets != null)
|
||||||
{
|
{
|
||||||
if (!Plugin.InRaid() || !Settings.EnableLoadAmmo.Value)
|
int count = GridView.smethod_0(magazine, bullets);
|
||||||
{
|
__result = inventoryController.LoadMagazine(bullets, magazine, count, false);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
InventoryControllerClass inventoryController = __instance.R().InventoryController;
|
|
||||||
EquipmentClass equipment = inventoryController.Inventory.Equipment;
|
|
||||||
|
|
||||||
List<BulletClass> ammo = [];
|
|
||||||
equipment.GetAllAssembledItems(ammo);
|
|
||||||
|
|
||||||
// Just do the first stack
|
|
||||||
BulletClass bullets = ammo.Where(a => a.TemplateId == ammoTemplateId && a.Parent.Container is not Slot)
|
|
||||||
.OrderBy(a => a.SpawnedInSession)
|
|
||||||
.ThenBy(a => a.StackObjectsCount)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (bullets != null)
|
|
||||||
{
|
|
||||||
int count = GridView.smethod_0(magazine, bullets);
|
|
||||||
__result = inventoryController.LoadMagazine(bullets, magazine, count, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
__result = Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
__result = Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,46 +5,45 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class LoadMagPresetsPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class LoadMagPresetsPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(MagazineBuildPresetClass), nameof(MagazineBuildPresetClass.smethod_0));
|
||||||
{
|
}
|
||||||
return AccessTools.Method(typeof(MagazineBuildPresetClass), nameof(MagazineBuildPresetClass.smethod_0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method returns a list of places to search for ammo. For whatever reason, it only looks
|
// This method returns a list of places to search for ammo. For whatever reason, it only looks
|
||||||
// in equipment if stash + sorting table are not present.
|
// in equipment if stash + sorting table are not present.
|
||||||
// Can't just add equipment because that includes equipped slots and it likes to pull the chambered bullet out of equipped guns
|
// Can't just add equipment because that includes equipped slots and it likes to pull the chambered bullet out of equipped guns
|
||||||
[PatchPostfix]
|
[PatchPostfix]
|
||||||
public static void Postfix(Inventory inventory, List<LootItemClass> __result)
|
public static void Postfix(Inventory inventory, List<LootItemClass> __result)
|
||||||
|
{
|
||||||
|
if (!__result.Contains(inventory.Equipment))
|
||||||
{
|
{
|
||||||
if (!__result.Contains(inventory.Equipment))
|
var vest = inventory.Equipment.GetSlot(EquipmentSlot.TacticalVest);
|
||||||
|
if (vest.ContainedItem is LootItemClass vestLootItem)
|
||||||
{
|
{
|
||||||
var vest = inventory.Equipment.GetSlot(EquipmentSlot.TacticalVest);
|
__result.Add(vestLootItem);
|
||||||
if (vest.ContainedItem is LootItemClass vestLootItem)
|
}
|
||||||
{
|
|
||||||
__result.Add(vestLootItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
var pockets = inventory.Equipment.GetSlot(EquipmentSlot.Pockets);
|
var pockets = inventory.Equipment.GetSlot(EquipmentSlot.Pockets);
|
||||||
if (pockets.ContainedItem is LootItemClass pocketsLootItem)
|
if (pockets.ContainedItem is LootItemClass pocketsLootItem)
|
||||||
{
|
{
|
||||||
__result.Add(pocketsLootItem);
|
__result.Add(pocketsLootItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
var backpack = inventory.Equipment.GetSlot(EquipmentSlot.Backpack);
|
var backpack = inventory.Equipment.GetSlot(EquipmentSlot.Backpack);
|
||||||
if (backpack.ContainedItem is LootItemClass backpackLootItem)
|
if (backpack.ContainedItem is LootItemClass backpackLootItem)
|
||||||
{
|
{
|
||||||
__result.Add(backpackLootItem);
|
__result.Add(backpackLootItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
var secureContainer = inventory.Equipment.GetSlot(EquipmentSlot.SecuredContainer);
|
var secureContainer = inventory.Equipment.GetSlot(EquipmentSlot.SecuredContainer);
|
||||||
if (secureContainer.ContainedItem is LootItemClass secureContainerLootItem)
|
if (secureContainer.ContainedItem is LootItemClass secureContainerLootItem)
|
||||||
{
|
{
|
||||||
__result.Add(secureContainerLootItem);
|
__result.Add(secureContainerLootItem);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,142 +9,141 @@ using System.Reflection;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class LoadMultipleMagazinesPatches
|
||||||
{
|
{
|
||||||
public static class LoadMultipleMagazinesPatches
|
private static ItemFilter[] CombinedFilters;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static ItemFilter[] CombinedFilters;
|
new FindCompatibleAmmoPatch().Enable();
|
||||||
|
new CheckItemFilterPatch().Enable();
|
||||||
|
new LoadAmmoPatch().Enable();
|
||||||
|
new FilterMagPresetsPatch().Enable();
|
||||||
|
new LoadPresetPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
public class FindCompatibleAmmoPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new FindCompatibleAmmoPatch().Enable();
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.FindCompatibleAmmo));
|
||||||
new CheckItemFilterPatch().Enable();
|
|
||||||
new LoadAmmoPatch().Enable();
|
|
||||||
new FilterMagPresetsPatch().Enable();
|
|
||||||
new LoadPresetPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FindCompatibleAmmoPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (MultiSelect.Active)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.FindCompatibleAmmo));
|
CombinedFilters = MultiSelect.SortedItemContexts()
|
||||||
}
|
.Select(itemContext => itemContext.Item)
|
||||||
|
.OfType<MagazineClass>()
|
||||||
[PatchPrefix]
|
.SelectMany(mag => mag.Cartridges.Filters)
|
||||||
public static void Prefix()
|
.ToArray();
|
||||||
{
|
|
||||||
if (MultiSelect.Active)
|
|
||||||
{
|
|
||||||
CombinedFilters = MultiSelect.SortedItemContexts()
|
|
||||||
.Select(itemContext => itemContext.Item)
|
|
||||||
.OfType<MagazineClass>()
|
|
||||||
.SelectMany(mag => mag.Cartridges.Filters)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
CombinedFilters = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CheckItemFilterPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
CombinedFilters = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CheckItemFilterPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ItemFilterExtensions), nameof(ItemFilterExtensions.CheckItemFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(ref ItemFilter[] filters)
|
||||||
|
{
|
||||||
|
if (CombinedFilters == null)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemFilterExtensions), nameof(ItemFilterExtensions.CheckItemFilter));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
filters = CombinedFilters;
|
||||||
public static void Prefix(ref ItemFilter[] filters)
|
}
|
||||||
{
|
}
|
||||||
if (CombinedFilters == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
filters = CombinedFilters;
|
public class LoadAmmoPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagInteraction") != null);
|
||||||
|
return AccessTools.Method(type, "method_6");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(string ammoTemplateId, ref Task __result, ItemUiContext ___itemUiContext_0)
|
||||||
|
{
|
||||||
|
if (!MultiSelect.Active)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__result = MultiSelect.LoadAmmoAll(___itemUiContext_0, ammoTemplateId, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FilterMagPresetsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null);
|
||||||
|
return AccessTools.Method(type, "method_7");
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix()
|
||||||
|
{
|
||||||
|
if (MultiSelect.Active)
|
||||||
|
{
|
||||||
|
CombinedFilters = MultiSelect.SortedItemContexts()
|
||||||
|
.Select(itemContext => itemContext.Item)
|
||||||
|
.OfType<MagazineClass>()
|
||||||
|
.SelectMany(mag => mag.Cartridges.Filters)
|
||||||
|
.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LoadAmmoPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
CombinedFilters = null;
|
||||||
{
|
}
|
||||||
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagInteraction") != null);
|
}
|
||||||
return AccessTools.Method(type, "method_6");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
public class LoadPresetPatch : ModulePatch
|
||||||
public static bool Prefix(string ammoTemplateId, ref Task __result, ItemUiContext ___itemUiContext_0)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
if (!MultiSelect.Active)
|
{
|
||||||
{
|
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null);
|
||||||
return true;
|
return AccessTools.Method(type, "method_6");
|
||||||
}
|
|
||||||
|
|
||||||
__result = MultiSelect.LoadAmmoAll(___itemUiContext_0, ammoTemplateId, false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FilterMagPresetsPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(MagazineBuildPresetClass preset, ItemUiContext ___itemUiContext_1)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!MultiSelect.Active)
|
||||||
{
|
{
|
||||||
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null);
|
return true;
|
||||||
return AccessTools.Method(type, "method_7");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
|
||||||
public static void Prefix()
|
|
||||||
{
|
{
|
||||||
if (MultiSelect.Active)
|
return true;
|
||||||
{
|
|
||||||
CombinedFilters = MultiSelect.SortedItemContexts()
|
|
||||||
.Select(itemContext => itemContext.Item)
|
|
||||||
.OfType<MagazineClass>()
|
|
||||||
.SelectMany(mag => mag.Cartridges.Filters)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
var magazines = MultiSelect.SortedItemContexts().Select(itemContext => itemContext.Item).OfType<MagazineClass>();
|
||||||
public static void Postfix()
|
___itemUiContext_1.ApplyMagPreset(preset, magazines.ToList()).HandleExceptions();
|
||||||
{
|
|
||||||
CombinedFilters = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LoadPresetPatch : ModulePatch
|
return false;
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
Type type = PatchConstants.EftTypes.Single(t => t.GetNestedType("EMagPresetInteraction") != null);
|
|
||||||
return AccessTools.Method(type, "method_6");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(MagazineBuildPresetClass preset, ItemUiContext ___itemUiContext_1)
|
|
||||||
{
|
|
||||||
if (!MultiSelect.Active)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var magazines = MultiSelect.SortedItemContexts().Select(itemContext => itemContext.Item).OfType<MagazineClass>();
|
|
||||||
___itemUiContext_1.ApplyMagPreset(preset, magazines.ToList()).HandleExceptions();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,20 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class MoveTaskbarPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class MoveTaskbarPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(MenuTaskBar), nameof(MenuTaskBar.Awake));
|
||||||
{
|
}
|
||||||
return AccessTools.Method(typeof(MenuTaskBar), nameof(MenuTaskBar.Awake));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
[PatchPostfix]
|
||||||
public static void Postfix(MenuTaskBar __instance)
|
public static void Postfix(MenuTaskBar __instance)
|
||||||
{
|
{
|
||||||
var bottomPanel = __instance.GetComponentsInParent<RectTransform>().First(c => c.name == "BottomPanel");
|
var bottomPanel = __instance.GetComponentsInParent<RectTransform>().First(c => c.name == "BottomPanel");
|
||||||
|
|
||||||
bottomPanel.localPosition = new Vector3(0f, -3f, 0f);
|
bottomPanel.localPosition = new Vector3(0f, -3f, 0f);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -4,56 +4,55 @@ using System.Collections.Generic;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class NoRandomGrenadesPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class NoRandomGrenadesPatch : ModulePatch
|
private static NoRandomGrenadesPatch Patch;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
{
|
{
|
||||||
private static NoRandomGrenadesPatch Patch;
|
Settings.DeterministicGrenades.Bind(enabled =>
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
{
|
||||||
Settings.DeterministicGrenades.Bind(enabled =>
|
if (enabled)
|
||||||
{
|
{
|
||||||
if (enabled)
|
Patch ??= new NoRandomGrenadesPatch();
|
||||||
{
|
Patch.Enable();
|
||||||
Patch ??= new NoRandomGrenadesPatch();
|
}
|
||||||
Patch.Enable();
|
else
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Patch?.Disable();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make ctor private so I don't forget to call Init() instead
|
|
||||||
private NoRandomGrenadesPatch() { }
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(Class1472), nameof(Class1472.vmethod_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchTranspiler]
|
|
||||||
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
foreach (var instruction in instructions)
|
|
||||||
{
|
{
|
||||||
if (instruction.opcode == OpCodes.Ble_S || instruction.opcode == OpCodes.Ble) // DnSpy is lying about which one this is!?
|
Patch?.Disable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make ctor private so I don't forget to call Init() instead
|
||||||
|
private NoRandomGrenadesPatch() { }
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(Class1472), nameof(Class1472.vmethod_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchTranspiler]
|
||||||
|
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
||||||
|
{
|
||||||
|
foreach (var instruction in instructions)
|
||||||
|
{
|
||||||
|
if (instruction.opcode == OpCodes.Ble_S || instruction.opcode == OpCodes.Ble) // DnSpy is lying about which one this is!?
|
||||||
|
{
|
||||||
|
// This is the line
|
||||||
|
// if (count > 0)
|
||||||
|
// which in IL does "if count is less than or equal to 1, jump over"
|
||||||
|
// So switch the IL to bge, so it jumps over if count is greater or equal to 1, thus skipping the randomizer
|
||||||
|
yield return new CodeInstruction(instruction)
|
||||||
{
|
{
|
||||||
// This is the line
|
opcode = instruction.opcode == OpCodes.Ble_S ? OpCodes.Bge_S : OpCodes.Bge,
|
||||||
// if (count > 0)
|
};
|
||||||
// which in IL does "if count is less than or equal to 1, jump over"
|
}
|
||||||
// So switch the IL to bge, so it jumps over if count is greater or equal to 1, thus skipping the randomizer
|
else
|
||||||
yield return new CodeInstruction(instruction)
|
{
|
||||||
{
|
yield return instruction;
|
||||||
opcode = instruction.opcode == OpCodes.Ble_S ? OpCodes.Bge_S : OpCodes.Bge,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
yield return instruction;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,45 +6,44 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class OpenSortingTablePatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class OpenSortingTablePatch : ModulePatch
|
private static readonly EItemUiContextType[] AllowedScreens = [EItemUiContextType.InventoryScreen, EItemUiContextType.ScavengerInventoryScreen];
|
||||||
|
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
private static readonly EItemUiContextType[] AllowedScreens = [EItemUiContextType.InventoryScreen, EItemUiContextType.ScavengerInventoryScreen];
|
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.QuickMoveToSortingTable));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
protected override MethodBase GetTargetMethod()
|
public static void Prefix(ItemUiContext __instance)
|
||||||
|
{
|
||||||
|
if (!Settings.AutoOpenSortingTable.Value || !AllowedScreens.Contains(__instance.ContextType) || Plugin.InRaid())
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(ItemUiContext), nameof(ItemUiContext.QuickMoveToSortingTable));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
// Temporary work-around for LootValue bug - bail out if the ALT key is down
|
||||||
public static void Prefix(ItemUiContext __instance)
|
if (Input.GetKey(KeyCode.LeftAlt))
|
||||||
{
|
{
|
||||||
if (!Settings.AutoOpenSortingTable.Value || !AllowedScreens.Contains(__instance.ContextType) || Plugin.InRaid())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary work-around for LootValue bug - bail out if the ALT key is down
|
SortingTableClass sortingTable = __instance.R().InventoryController.Inventory.SortingTable;
|
||||||
if (Input.GetKey(KeyCode.LeftAlt))
|
if (sortingTable != null && !sortingTable.IsVisible)
|
||||||
|
{
|
||||||
|
if (__instance.ContextType == EItemUiContextType.InventoryScreen)
|
||||||
{
|
{
|
||||||
return;
|
Singleton<CommonUI>.Instance.InventoryScreen.method_6();
|
||||||
|
Singleton<CommonUI>.Instance.InventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
||||||
}
|
}
|
||||||
|
else if (__instance.ContextType == EItemUiContextType.ScavengerInventoryScreen)
|
||||||
SortingTableClass sortingTable = __instance.R().InventoryController.Inventory.SortingTable;
|
|
||||||
if (sortingTable != null && !sortingTable.IsVisible)
|
|
||||||
{
|
{
|
||||||
if (__instance.ContextType == EItemUiContextType.InventoryScreen)
|
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.method_7();
|
||||||
{
|
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
||||||
Singleton<CommonUI>.Instance.InventoryScreen.method_6();
|
|
||||||
Singleton<CommonUI>.Instance.InventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
|
||||||
}
|
|
||||||
else if (__instance.ContextType == EItemUiContextType.ScavengerInventoryScreen)
|
|
||||||
{
|
|
||||||
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.method_7();
|
|
||||||
Singleton<CommonUI>.Instance.ScavengerInventoryScreen.R().SimpleStashPanel.ChangeSortingTableTabState(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,62 +7,61 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class PutToolsBackPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class PutToolsBackPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(R.ItemReceiver.Type, "method_9"); // GClass1855
|
||||||
{
|
}
|
||||||
return AccessTools.Method(R.ItemReceiver.Type, "method_9"); // GClass1855
|
|
||||||
}
|
|
||||||
|
|
||||||
// The patched method can't handle new items that aren't in stash root.
|
// The patched method can't handle new items that aren't in stash root.
|
||||||
// Find items that are in subcontainers and handle them first - the patched method will ignore items that have a CurrentAddress
|
// Find items that are in subcontainers and handle them first - the patched method will ignore items that have a CurrentAddress
|
||||||
// This is a subset of the original method - doesn't handle slots, equipment containers, etc.
|
// This is a subset of the original method - doesn't handle slots, equipment containers, etc.
|
||||||
[PatchPrefix]
|
[PatchPrefix]
|
||||||
public static void Prefix(object __instance, ref JsonItem[] newItems, Profile ___profile_0, ItemFactory ___itemFactory)
|
public static void Prefix(object __instance, ref JsonItem[] newItems, Profile ___profile_0, ItemFactory ___itemFactory)
|
||||||
|
{
|
||||||
|
Inventory inventory = ___profile_0.Inventory;
|
||||||
|
StashClass stash = inventory.Stash;
|
||||||
|
if (inventory != null && stash != null)
|
||||||
{
|
{
|
||||||
Inventory inventory = ___profile_0.Inventory;
|
// Handled items are either in these top level containers or are nested inside each other (mods, attachments, etc)
|
||||||
StashClass stash = inventory.Stash;
|
var handledContainerIds = newItems.Select(i => i._id).Concat([inventory.Stash.Id, inventory.Equipment.Id, inventory.QuestRaidItems.Id, inventory.QuestStashItems.Id, inventory.SortingTable.Id]);
|
||||||
if (inventory != null && stash != null)
|
var unhandledItems = newItems.Where(i => !String.IsNullOrEmpty(i.parentId) && !handledContainerIds.Contains(i.parentId));
|
||||||
|
|
||||||
|
if (!unhandledItems.Any())
|
||||||
{
|
{
|
||||||
// Handled items are either in these top level containers or are nested inside each other (mods, attachments, etc)
|
return;
|
||||||
var handledContainerIds = newItems.Select(i => i._id).Concat([inventory.Stash.Id, inventory.Equipment.Id, inventory.QuestRaidItems.Id, inventory.QuestStashItems.Id, inventory.SortingTable.Id]);
|
}
|
||||||
var unhandledItems = newItems.Where(i => !String.IsNullOrEmpty(i.parentId) && !handledContainerIds.Contains(i.parentId));
|
|
||||||
|
|
||||||
if (!unhandledItems.Any())
|
// Change the parameter to remove the items handled here
|
||||||
|
newItems = newItems.Except(unhandledItems).ToArray();
|
||||||
|
|
||||||
|
List<Item> stashItems = stash.GetNotMergedItems().ToList();
|
||||||
|
|
||||||
|
InventoryControllerClass inventoryController = new R.ItemReceiver(__instance).InventoryController;
|
||||||
|
|
||||||
|
var tree = ___itemFactory.FlatItemsToTree(unhandledItems.ToArray(), true, null);
|
||||||
|
foreach (Item item in tree.Items.Values.Where(i => i.CurrentAddress == null))
|
||||||
|
{
|
||||||
|
var newItem = unhandledItems.First(i => i._id == item.Id);
|
||||||
|
if (newItem.parentId != null || newItem.slotId != null)
|
||||||
{
|
{
|
||||||
return;
|
// Assuming here that unhandled items are trying to go into containers in the stash - find that container
|
||||||
}
|
Item parent = stashItems.FirstOrDefault(i => i.Id == newItem.parentId);
|
||||||
|
if (parent is ContainerCollection containerCollection)
|
||||||
// Change the parameter to remove the items handled here
|
|
||||||
newItems = newItems.Except(unhandledItems).ToArray();
|
|
||||||
|
|
||||||
List<Item> stashItems = stash.GetNotMergedItems().ToList();
|
|
||||||
|
|
||||||
InventoryControllerClass inventoryController = new R.ItemReceiver(__instance).InventoryController;
|
|
||||||
|
|
||||||
var tree = ___itemFactory.FlatItemsToTree(unhandledItems.ToArray(), true, null);
|
|
||||||
foreach (Item item in tree.Items.Values.Where(i => i.CurrentAddress == null))
|
|
||||||
{
|
|
||||||
var newItem = unhandledItems.First(i => i._id == item.Id);
|
|
||||||
if (newItem.parentId != null || newItem.slotId != null)
|
|
||||||
{
|
{
|
||||||
// Assuming here that unhandled items are trying to go into containers in the stash - find that container
|
if (containerCollection.GetContainer(newItem.slotId) is StashGridClass grid)
|
||||||
Item parent = stashItems.FirstOrDefault(i => i.Id == newItem.parentId);
|
|
||||||
if (parent is ContainerCollection containerCollection)
|
|
||||||
{
|
{
|
||||||
if (containerCollection.GetContainer(newItem.slotId) is StashGridClass grid)
|
LocationInGrid location = LocationJsonParser.CreateItemLocation<LocationInGrid>(newItem.location);
|
||||||
{
|
ItemAddress itemAddress = new GridItemAddress(grid, location);
|
||||||
LocationInGrid location = LocationJsonParser.CreateItemLocation<LocationInGrid>(newItem.location);
|
|
||||||
ItemAddress itemAddress = new GridItemAddress(grid, location);
|
|
||||||
|
|
||||||
var operation = InteractionsHandlerClass.Add(item, itemAddress, inventoryController, false);
|
var operation = InteractionsHandlerClass.Add(item, itemAddress, inventoryController, false);
|
||||||
if (operation.Succeeded)
|
if (operation.Succeeded)
|
||||||
{
|
{
|
||||||
operation.Value.RaiseEvents(inventoryController, CommandStatus.Begin);
|
operation.Value.RaiseEvents(inventoryController, CommandStatus.Begin);
|
||||||
operation.Value.RaiseEvents(inventoryController, CommandStatus.Succeed);
|
operation.Value.RaiseEvents(inventoryController, CommandStatus.Succeed);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,98 +8,97 @@ using HarmonyLib;
|
|||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class QuickAccessPanelPatches
|
||||||
{
|
{
|
||||||
public static class QuickAccessPanelPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new FixWeaponBindsDisplayPatch().Enable();
|
||||||
|
new FixVisibilityPatch().Enable();
|
||||||
|
new TranslateCommandHackPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FixWeaponBindsDisplayPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new FixWeaponBindsDisplayPatch().Enable();
|
return AccessTools.Method(R.ControlSettings.Type, "GetBoundItemNames");
|
||||||
new FixVisibilityPatch().Enable();
|
|
||||||
new TranslateCommandHackPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FixWeaponBindsDisplayPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(object __instance, EBoundItem boundItem, ref string __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var instance = new R.ControlSettings(__instance);
|
||||||
|
switch (boundItem)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(R.ControlSettings.Type, "GetBoundItemNames");
|
case EBoundItem.Item1:
|
||||||
}
|
__result = instance.GetKeyName(EGameKey.SecondaryWeapon);
|
||||||
|
break;
|
||||||
[PatchPostfix]
|
case EBoundItem.Item2:
|
||||||
public static void Postfix(object __instance, EBoundItem boundItem, ref string __result)
|
__result = instance.GetKeyName(EGameKey.PrimaryWeaponFirst);
|
||||||
{
|
break;
|
||||||
var instance = new R.ControlSettings(__instance);
|
case EBoundItem.Item3:
|
||||||
switch (boundItem)
|
__result = instance.GetKeyName(EGameKey.PrimaryWeaponSecond);
|
||||||
{
|
break;
|
||||||
case EBoundItem.Item1:
|
|
||||||
__result = instance.GetKeyName(EGameKey.SecondaryWeapon);
|
|
||||||
break;
|
|
||||||
case EBoundItem.Item2:
|
|
||||||
__result = instance.GetKeyName(EGameKey.PrimaryWeaponFirst);
|
|
||||||
break;
|
|
||||||
case EBoundItem.Item3:
|
|
||||||
__result = instance.GetKeyName(EGameKey.PrimaryWeaponSecond);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FixVisibilityPatch : ModulePatch
|
|
||||||
{
|
|
||||||
public static bool Ignorable = false;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(InventoryScreenQuickAccessPanel), nameof(InventoryScreenQuickAccessPanel.method_4));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method is a mess. The visibility setting has to be ignored in some cases, respected in others
|
|
||||||
// In most calls, visible=true must be followed regardless of setting preference, e.g. mag selection
|
|
||||||
// When coming from translatecommand, which is when you hit a quickbind key, visible=true can be ignored if the setting is never
|
|
||||||
// Ironically this is also the only time that autohide matters, since the other places will explicitly call hide
|
|
||||||
// visible=false can always be ignored if setting is always
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(InventoryScreenQuickAccessPanel __instance, bool visible)
|
|
||||||
{
|
|
||||||
GameSetting<EVisibilityMode> quickSlotsVisibility = Singleton<SharedGameSettingsClass>.Instance.Game.Settings.QuickSlotsVisibility;
|
|
||||||
|
|
||||||
bool shouldShow = visible && !__instance.IsDisabled;
|
|
||||||
bool blocked = Ignorable && quickSlotsVisibility == EVisibilityMode.Never;
|
|
||||||
|
|
||||||
if (shouldShow && !blocked)
|
|
||||||
{
|
|
||||||
bool autohide = Ignorable && quickSlotsVisibility == EVisibilityMode.Autohide;
|
|
||||||
__instance.AnimatedShow(autohide);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (!shouldShow && quickSlotsVisibility != EVisibilityMode.Always)
|
|
||||||
{
|
|
||||||
__instance.AnimatedHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TranslateCommandHackPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(InventoryScreenQuickAccessPanel), nameof(InventoryScreenQuickAccessPanel.TranslateCommand));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(ECommand command)
|
|
||||||
{
|
|
||||||
FixVisibilityPatch.Ignorable = QuickBindCommandMap.SlotBySelectCommandDictionary.ContainsKey(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix()
|
|
||||||
{
|
|
||||||
FixVisibilityPatch.Ignorable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class FixVisibilityPatch : ModulePatch
|
||||||
|
{
|
||||||
|
public static bool Ignorable = false;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(InventoryScreenQuickAccessPanel), nameof(InventoryScreenQuickAccessPanel.method_4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method is a mess. The visibility setting has to be ignored in some cases, respected in others
|
||||||
|
// In most calls, visible=true must be followed regardless of setting preference, e.g. mag selection
|
||||||
|
// When coming from translatecommand, which is when you hit a quickbind key, visible=true can be ignored if the setting is never
|
||||||
|
// Ironically this is also the only time that autohide matters, since the other places will explicitly call hide
|
||||||
|
// visible=false can always be ignored if setting is always
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(InventoryScreenQuickAccessPanel __instance, bool visible)
|
||||||
|
{
|
||||||
|
GameSetting<EVisibilityMode> quickSlotsVisibility = Singleton<SharedGameSettingsClass>.Instance.Game.Settings.QuickSlotsVisibility;
|
||||||
|
|
||||||
|
bool shouldShow = visible && !__instance.IsDisabled;
|
||||||
|
bool blocked = Ignorable && quickSlotsVisibility == EVisibilityMode.Never;
|
||||||
|
|
||||||
|
if (shouldShow && !blocked)
|
||||||
|
{
|
||||||
|
bool autohide = Ignorable && quickSlotsVisibility == EVisibilityMode.Autohide;
|
||||||
|
__instance.AnimatedShow(autohide);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (!shouldShow && quickSlotsVisibility != EVisibilityMode.Always)
|
||||||
|
{
|
||||||
|
__instance.AnimatedHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TranslateCommandHackPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(InventoryScreenQuickAccessPanel), nameof(InventoryScreenQuickAccessPanel.TranslateCommand));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(ECommand command)
|
||||||
|
{
|
||||||
|
FixVisibilityPatch.Ignorable = QuickBindCommandMap.SlotBySelectCommandDictionary.ContainsKey(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix()
|
||||||
|
{
|
||||||
|
FixVisibilityPatch.Ignorable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,41 +7,40 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
{
|
|
||||||
public class RebindGrenadesPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static readonly EquipmentSlot[] Slots = [EquipmentSlot.Pockets, EquipmentSlot.TacticalVest, EquipmentSlot.Backpack, EquipmentSlot.SecuredContainer, EquipmentSlot.ArmBand];
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
public class RebindGrenadesPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static readonly EquipmentSlot[] Slots = [EquipmentSlot.Pockets, EquipmentSlot.TacticalVest, EquipmentSlot.Backpack, EquipmentSlot.SecuredContainer, EquipmentSlot.ArmBand];
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
Type type = typeof(Player).GetNestedTypes().Single(t => t.GetField("DiscardResult") != null);
|
||||||
|
return AccessTools.Method(type, "RaiseEvents");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a grenade specific event emitter that has all the info needed to do this
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(CommandStatus status, DiscardResult ___DiscardResult)
|
||||||
|
{
|
||||||
|
if (status != CommandStatus.Succeed)
|
||||||
{
|
{
|
||||||
Type type = typeof(Player).GetNestedTypes().Single(t => t.GetField("DiscardResult") != null);
|
return;
|
||||||
return AccessTools.Method(type, "RaiseEvents");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a grenade specific event emitter that has all the info needed to do this
|
var unbindResult = ___DiscardResult.UnbindResults.FirstOrDefault();
|
||||||
[PatchPostfix]
|
if (unbindResult != null)
|
||||||
public static void Postfix(CommandStatus status, DiscardResult ___DiscardResult)
|
|
||||||
{
|
{
|
||||||
if (status != CommandStatus.Succeed)
|
InventoryControllerClass controller = unbindResult.Controller;
|
||||||
|
EBoundItem index = unbindResult.Index;
|
||||||
|
|
||||||
|
List<GrenadeClass> matchingGrenades = [];
|
||||||
|
controller.GetAcceptableItemsNonAlloc<GrenadeClass>(Slots, matchingGrenades, g => g.TemplateId == unbindResult.Item.TemplateId);
|
||||||
|
|
||||||
|
var nextGrenade = matchingGrenades.FirstOrDefault(g => controller.IsAtBindablePlace(g));
|
||||||
|
if (nextGrenade != null)
|
||||||
{
|
{
|
||||||
return;
|
controller.TryRunNetworkTransaction(BindOperation.Run(controller, nextGrenade, index, true), null);
|
||||||
}
|
|
||||||
|
|
||||||
var unbindResult = ___DiscardResult.UnbindResults.FirstOrDefault();
|
|
||||||
if (unbindResult != null)
|
|
||||||
{
|
|
||||||
InventoryControllerClass controller = unbindResult.Controller;
|
|
||||||
EBoundItem index = unbindResult.Index;
|
|
||||||
|
|
||||||
List<GrenadeClass> matchingGrenades = [];
|
|
||||||
controller.GetAcceptableItemsNonAlloc<GrenadeClass>(Slots, matchingGrenades, g => g.TemplateId == unbindResult.Item.TemplateId);
|
|
||||||
|
|
||||||
var nextGrenade = matchingGrenades.FirstOrDefault(g => controller.IsAtBindablePlace(g));
|
|
||||||
if (nextGrenade != null)
|
|
||||||
{
|
|
||||||
controller.TryRunNetworkTransaction(BindOperation.Run(controller, nextGrenade, index, true), null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,49 +4,48 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class RememberRepairerPatches
|
||||||
{
|
{
|
||||||
public static class RememberRepairerPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new RememberRepairerPatch().Enable();
|
||||||
|
new DefaultMaxRepairPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RememberRepairerPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static readonly string PlayerPrefKey = "UIFixes.Repair.CurrentRepairerIndex";
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new RememberRepairerPatch().Enable();
|
return AccessTools.Method(typeof(RepairerParametersPanel), nameof(RepairerParametersPanel.Show));
|
||||||
new DefaultMaxRepairPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RememberRepairerPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(RepairerParametersPanel __instance, DropDownBox ____tradersDropDown)
|
||||||
{
|
{
|
||||||
private static readonly string PlayerPrefKey = "UIFixes.Repair.CurrentRepairerIndex";
|
__instance.R().UI.AddDisposable(____tradersDropDown.OnValueChanged.Subscribe(index => PlayerPrefs.SetInt(PlayerPrefKey, index)));
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
if (PlayerPrefs.HasKey(PlayerPrefKey))
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(RepairerParametersPanel), nameof(RepairerParametersPanel.Show));
|
____tradersDropDown.UpdateValue(PlayerPrefs.GetInt(PlayerPrefKey));
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(RepairerParametersPanel __instance, DropDownBox ____tradersDropDown)
|
|
||||||
{
|
|
||||||
__instance.R().UI.AddDisposable(____tradersDropDown.OnValueChanged.Subscribe(index => PlayerPrefs.SetInt(PlayerPrefKey, index)));
|
|
||||||
|
|
||||||
if (PlayerPrefs.HasKey(PlayerPrefKey))
|
|
||||||
{
|
|
||||||
____tradersDropDown.UpdateValue(PlayerPrefs.GetInt(PlayerPrefKey));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DefaultMaxRepairPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(RepairerParametersPanel), nameof(RepairerParametersPanel.method_3));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(ConditionCharacteristicsSlider ____conditionSlider)
|
|
||||||
{
|
|
||||||
____conditionSlider.method_1(); // like clicking >>, aka select max value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DefaultMaxRepairPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(RepairerParametersPanel), nameof(RepairerParametersPanel.method_3));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ConditionCharacteristicsSlider ____conditionSlider)
|
||||||
|
{
|
||||||
|
____conditionSlider.method_1(); // like clicking >>, aka select max value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,33 +4,32 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class RemoveDoorActionsPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class RemoveDoorActionsPatch : ModulePatch
|
private static readonly string[] UnimplementedActions = ["Bang & clear", "Flash & clear", "Move in"];
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
private static readonly string[] UnimplementedActions = ["Bang & clear", "Flash & clear", "Move in"];
|
Type type = typeof(GetActionsClass);
|
||||||
|
return AccessTools.GetDeclaredMethods(type).FirstOrDefault(x =>
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
Type type = typeof(GetActionsClass);
|
var parameters = x.GetParameters();
|
||||||
return AccessTools.GetDeclaredMethods(type).FirstOrDefault(x =>
|
return x.Name == nameof(GetActionsClass.GetAvailableActions) && parameters[0].Name == "owner";
|
||||||
{
|
});
|
||||||
var parameters = x.GetParameters();
|
}
|
||||||
return x.Name == nameof(GetActionsClass.GetAvailableActions) && parameters[0].Name == "owner";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
[PatchPostfix]
|
||||||
public static void Postfix(ref ActionsReturnClass __result)
|
public static void Postfix(ref ActionsReturnClass __result)
|
||||||
|
{
|
||||||
|
if (Settings.RemoveDisabledActions.Value && __result != null)
|
||||||
{
|
{
|
||||||
if (Settings.RemoveDisabledActions.Value && __result != null)
|
for (int i = __result.Actions.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
for (int i = __result.Actions.Count - 1; i >= 0; i--)
|
if (UnimplementedActions.Contains(__result.Actions[i].Name))
|
||||||
{
|
{
|
||||||
if (UnimplementedActions.Contains(__result.Actions[i].Name))
|
__result.Actions.RemoveAt(i);
|
||||||
{
|
|
||||||
__result.Actions.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,137 +7,136 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class ReorderGridsPatches
|
||||||
{
|
{
|
||||||
public static class ReorderGridsPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new ReorderGridsPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There are 3 cases to handle in TemplatedGridsView.Show
|
||||||
|
* 1. An item is shown for the first time
|
||||||
|
* - It renders on its own, and the UI is correct
|
||||||
|
* - Use the UI to sort Grids, and update GridViews to match
|
||||||
|
* 2. An item is shown for the 2nd+ time in a new context
|
||||||
|
* - The GridViews will be recreated, so in the prefix we need to reorder them to match the Grids order
|
||||||
|
* 3. An existing TemplatedGridsView is reshown
|
||||||
|
* - Everything is already in place, no action needed
|
||||||
|
*/
|
||||||
|
public class ReorderGridsPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, int[]> GridMaps = [];
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new ReorderGridsPatch().Enable();
|
return AccessTools.DeclaredMethod(typeof(TemplatedGridsView), nameof(TemplatedGridsView.Show));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There are 3 cases to handle in TemplatedGridsView.Show
|
[PatchPrefix]
|
||||||
* 1. An item is shown for the first time
|
public static void Prefix(TemplatedGridsView __instance, LootItemClass compoundItem, ref GridView[] ____presetGridViews)
|
||||||
* - It renders on its own, and the UI is correct
|
|
||||||
* - Use the UI to sort Grids, and update GridViews to match
|
|
||||||
* 2. An item is shown for the 2nd+ time in a new context
|
|
||||||
* - The GridViews will be recreated, so in the prefix we need to reorder them to match the Grids order
|
|
||||||
* 3. An existing TemplatedGridsView is reshown
|
|
||||||
* - Everything is already in place, no action needed
|
|
||||||
*/
|
|
||||||
public class ReorderGridsPatch : ModulePatch
|
|
||||||
{
|
{
|
||||||
private static readonly Dictionary<string, int[]> GridMaps = [];
|
if (!Settings.ReorderGrids.Value)
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
return AccessTools.DeclaredMethod(typeof(TemplatedGridsView), nameof(TemplatedGridsView.Show));
|
// To properly support disabling this feature:
|
||||||
}
|
// 1. Items that sorted their Grids need to return them to original order
|
||||||
|
// 2. If this TemplatedGridsView was sorted, it needs to be unsorted to match
|
||||||
[PatchPrefix]
|
if (compoundItem.GetReordered() && GridMaps.TryGetValue(compoundItem.TemplateId, out int[] unwantedMap))
|
||||||
public static void Prefix(TemplatedGridsView __instance, LootItemClass compoundItem, ref GridView[] ____presetGridViews)
|
|
||||||
{
|
|
||||||
if (!Settings.ReorderGrids.Value)
|
|
||||||
{
|
{
|
||||||
// To properly support disabling this feature:
|
StashGridClass[] orderedGrids = new StashGridClass[compoundItem.Grids.Length];
|
||||||
// 1. Items that sorted their Grids need to return them to original order
|
for (int i = 0; i < compoundItem.Grids.Length; i++)
|
||||||
// 2. If this TemplatedGridsView was sorted, it needs to be unsorted to match
|
|
||||||
if (compoundItem.GetReordered() && GridMaps.TryGetValue(compoundItem.TemplateId, out int[] unwantedMap))
|
|
||||||
{
|
{
|
||||||
StashGridClass[] orderedGrids = new StashGridClass[compoundItem.Grids.Length];
|
orderedGrids[i] = compoundItem.Grids[unwantedMap[i]];
|
||||||
for (int i = 0; i < compoundItem.Grids.Length; i++)
|
|
||||||
{
|
|
||||||
orderedGrids[i] = compoundItem.Grids[unwantedMap[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
compoundItem.Grids = orderedGrids;
|
|
||||||
compoundItem.SetReordered(false);
|
|
||||||
|
|
||||||
if (__instance.GetReordered())
|
|
||||||
{
|
|
||||||
GridView[] orderedGridView = new GridView[____presetGridViews.Length];
|
|
||||||
for (int i = 0; i < ____presetGridViews.Length; i++)
|
|
||||||
{
|
|
||||||
orderedGridView[i] = ____presetGridViews[unwantedMap[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
____presetGridViews = orderedGridView;
|
|
||||||
__instance.SetReordered(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
compoundItem.Grids = orderedGrids;
|
||||||
}
|
compoundItem.SetReordered(false);
|
||||||
|
|
||||||
if (compoundItem.GetReordered() && !__instance.GetReordered())
|
if (__instance.GetReordered())
|
||||||
{
|
|
||||||
// This is a new context of a sorted Item, need to presort the GridViews
|
|
||||||
if (GridMaps.TryGetValue(compoundItem.TemplateId, out int[] map))
|
|
||||||
{
|
{
|
||||||
GridView[] orderedGridView = new GridView[____presetGridViews.Length];
|
GridView[] orderedGridView = new GridView[____presetGridViews.Length];
|
||||||
for (int i = 0; i < ____presetGridViews.Length; i++)
|
for (int i = 0; i < ____presetGridViews.Length; i++)
|
||||||
{
|
{
|
||||||
orderedGridView[map[i]] = ____presetGridViews[i];
|
orderedGridView[i] = ____presetGridViews[unwantedMap[i]];
|
||||||
}
|
}
|
||||||
|
|
||||||
____presetGridViews = orderedGridView;
|
____presetGridViews = orderedGridView;
|
||||||
__instance.SetReordered(true);
|
__instance.SetReordered(false);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.LogError($"Item {compoundItem.Id}, tpl: {compoundItem.TemplateId} has sorted Grids but no map to sort GridViews!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
if (compoundItem.GetReordered() && !__instance.GetReordered())
|
||||||
public static void Postfix(TemplatedGridsView __instance, LootItemClass compoundItem, ref GridView[] ____presetGridViews)
|
|
||||||
{
|
{
|
||||||
if (!Settings.ReorderGrids.Value || compoundItem.GetReordered())
|
// This is a new context of a sorted Item, need to presort the GridViews
|
||||||
|
if (GridMaps.TryGetValue(compoundItem.TemplateId, out int[] map))
|
||||||
{
|
{
|
||||||
return;
|
GridView[] orderedGridView = new GridView[____presetGridViews.Length];
|
||||||
}
|
|
||||||
|
|
||||||
var pairs = compoundItem.Grids.Zip(____presetGridViews, (g, gv) => new KeyValuePair<StashGridClass, GridView>(g, gv));
|
|
||||||
|
|
||||||
RectTransform parentView = __instance.RectTransform();
|
|
||||||
Vector2 parentPosition = parentView.pivot.y == 1 ? parentView.position : new Vector2(parentView.position.x, parentView.position.y + parentView.sizeDelta.y);
|
|
||||||
Vector2 gridSize = new(64f * parentView.lossyScale.x, 64f * parentView.lossyScale.y);
|
|
||||||
|
|
||||||
var sorted = pairs.OrderBy(pair =>
|
|
||||||
{
|
|
||||||
var grid = pair.Key;
|
|
||||||
var gridView = pair.Value;
|
|
||||||
|
|
||||||
float xOffset = gridView.transform.position.x - parentPosition.x;
|
|
||||||
float yOffset = -(gridView.transform.position.y - parentPosition.y); // invert y since grid coords are upper-left origin
|
|
||||||
|
|
||||||
int x = (int)Math.Round(xOffset / gridSize.x, MidpointRounding.AwayFromZero);
|
|
||||||
int y = (int)Math.Round(yOffset / gridSize.y, MidpointRounding.AwayFromZero);
|
|
||||||
|
|
||||||
return y * 100 + x;
|
|
||||||
});
|
|
||||||
|
|
||||||
GridView[] orderedGridViews = sorted.Select(pair => pair.Value).ToArray();
|
|
||||||
|
|
||||||
// Populate the gridmap
|
|
||||||
if (!GridMaps.ContainsKey(compoundItem.TemplateId))
|
|
||||||
{
|
|
||||||
int[] map = new int[____presetGridViews.Length];
|
|
||||||
for (int i = 0; i < ____presetGridViews.Length; i++)
|
for (int i = 0; i < ____presetGridViews.Length; i++)
|
||||||
{
|
{
|
||||||
map[i] = orderedGridViews.IndexOf(____presetGridViews[i]);
|
orderedGridView[map[i]] = ____presetGridViews[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
GridMaps.Add(compoundItem.TemplateId, map);
|
____presetGridViews = orderedGridView;
|
||||||
|
__instance.SetReordered(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogError($"Item {compoundItem.Id}, tpl: {compoundItem.TemplateId} has sorted Grids but no map to sort GridViews!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(TemplatedGridsView __instance, LootItemClass compoundItem, ref GridView[] ____presetGridViews)
|
||||||
|
{
|
||||||
|
if (!Settings.ReorderGrids.Value || compoundItem.GetReordered())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pairs = compoundItem.Grids.Zip(____presetGridViews, (g, gv) => new KeyValuePair<StashGridClass, GridView>(g, gv));
|
||||||
|
|
||||||
|
RectTransform parentView = __instance.RectTransform();
|
||||||
|
Vector2 parentPosition = parentView.pivot.y == 1 ? parentView.position : new Vector2(parentView.position.x, parentView.position.y + parentView.sizeDelta.y);
|
||||||
|
Vector2 gridSize = new(64f * parentView.lossyScale.x, 64f * parentView.lossyScale.y);
|
||||||
|
|
||||||
|
var sorted = pairs.OrderBy(pair =>
|
||||||
|
{
|
||||||
|
var grid = pair.Key;
|
||||||
|
var gridView = pair.Value;
|
||||||
|
|
||||||
|
float xOffset = gridView.transform.position.x - parentPosition.x;
|
||||||
|
float yOffset = -(gridView.transform.position.y - parentPosition.y); // invert y since grid coords are upper-left origin
|
||||||
|
|
||||||
|
int x = (int)Math.Round(xOffset / gridSize.x, MidpointRounding.AwayFromZero);
|
||||||
|
int y = (int)Math.Round(yOffset / gridSize.y, MidpointRounding.AwayFromZero);
|
||||||
|
|
||||||
|
return y * 100 + x;
|
||||||
|
});
|
||||||
|
|
||||||
|
GridView[] orderedGridViews = sorted.Select(pair => pair.Value).ToArray();
|
||||||
|
|
||||||
|
// Populate the gridmap
|
||||||
|
if (!GridMaps.ContainsKey(compoundItem.TemplateId))
|
||||||
|
{
|
||||||
|
int[] map = new int[____presetGridViews.Length];
|
||||||
|
for (int i = 0; i < ____presetGridViews.Length; i++)
|
||||||
|
{
|
||||||
|
map[i] = orderedGridViews.IndexOf(____presetGridViews[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
compoundItem.Grids = sorted.Select(pair => pair.Key).ToArray();
|
GridMaps.Add(compoundItem.TemplateId, map);
|
||||||
____presetGridViews = orderedGridViews;
|
|
||||||
|
|
||||||
compoundItem.SetReordered(true);
|
|
||||||
__instance.SetReordered(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compoundItem.Grids = sorted.Select(pair => pair.Key).ToArray();
|
||||||
|
____presetGridViews = orderedGridViews;
|
||||||
|
|
||||||
|
compoundItem.SetReordered(true);
|
||||||
|
__instance.SetReordered(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,353 +13,352 @@ using UnityEngine.Events;
|
|||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class ScrollPatches
|
||||||
{
|
{
|
||||||
public static class ScrollPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new EnhanceStashScrollingPatch().Enable();
|
||||||
{
|
new EnchanceTraderStashScrollingPatch().Enable();
|
||||||
new EnhanceStashScrollingPatch().Enable();
|
new EnhanceFleaScrollingPatch().Enable();
|
||||||
new EnchanceTraderStashScrollingPatch().Enable();
|
new EnhanceMailScrollingPatch().Enable();
|
||||||
new EnhanceFleaScrollingPatch().Enable();
|
new MouseScrollingSpeedPatch().Enable();
|
||||||
new EnhanceMailScrollingPatch().Enable();
|
new EnhanceHideoutScrollingPatch().Enable();
|
||||||
new MouseScrollingSpeedPatch().Enable();
|
new EnhanceTaskListScrollingPatch().Enable();
|
||||||
new EnhanceHideoutScrollingPatch().Enable();
|
new OpenLastTaskPatch().Enable();
|
||||||
new EnhanceTaskListScrollingPatch().Enable();
|
}
|
||||||
new OpenLastTaskPatch().Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool HandleInput(ScrollRect scrollRect)
|
private static bool HandleInput(ScrollRect scrollRect)
|
||||||
|
{
|
||||||
|
if (scrollRect != null)
|
||||||
{
|
{
|
||||||
if (scrollRect != null)
|
if (Settings.UseHomeEnd.Value)
|
||||||
{
|
{
|
||||||
if (Settings.UseHomeEnd.Value)
|
if (Input.GetKeyDown(KeyCode.Home))
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.Home))
|
scrollRect.verticalNormalizedPosition = 1f;
|
||||||
{
|
return true;
|
||||||
scrollRect.verticalNormalizedPosition = 1f;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (Input.GetKeyDown(KeyCode.End))
|
|
||||||
{
|
|
||||||
scrollRect.verticalNormalizedPosition = 0f;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (Input.GetKeyDown(KeyCode.End))
|
||||||
if (Settings.RebindPageUpDown.Value)
|
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.PageUp))
|
scrollRect.verticalNormalizedPosition = 0f;
|
||||||
{
|
return true;
|
||||||
// Duplicate this code to avoid running it every frame
|
|
||||||
Rect contentRect = scrollRect.content.rect;
|
|
||||||
Rect viewRect = scrollRect.RectTransform().rect;
|
|
||||||
float pageSize = viewRect.height / contentRect.height;
|
|
||||||
|
|
||||||
|
|
||||||
scrollRect.verticalNormalizedPosition = Math.Min(1f, scrollRect.verticalNormalizedPosition + pageSize);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Input.GetKeyDown(KeyCode.PageDown))
|
|
||||||
{
|
|
||||||
// Duplicate this code to avoid running it every frame
|
|
||||||
Rect contentRect = scrollRect.content.rect;
|
|
||||||
Rect viewRect = scrollRect.RectTransform().rect;
|
|
||||||
float pageSize = viewRect.height / contentRect.height;
|
|
||||||
|
|
||||||
|
|
||||||
scrollRect.verticalNormalizedPosition = Math.Max(0f, scrollRect.verticalNormalizedPosition - pageSize);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (Settings.RebindPageUpDown.Value)
|
||||||
}
|
|
||||||
|
|
||||||
// LightScrollers don't expose heights that I can see, so just fudge it with fake OnScroll events
|
|
||||||
private static bool HandleInput(LightScroller lightScroller)
|
|
||||||
{
|
|
||||||
if (lightScroller != null)
|
|
||||||
{
|
{
|
||||||
if (Settings.UseHomeEnd.Value)
|
if (Input.GetKeyDown(KeyCode.PageUp))
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.Home))
|
// Duplicate this code to avoid running it every frame
|
||||||
{
|
Rect contentRect = scrollRect.content.rect;
|
||||||
lightScroller.SetScrollPosition(0f);
|
Rect viewRect = scrollRect.RectTransform().rect;
|
||||||
return true;
|
float pageSize = viewRect.height / contentRect.height;
|
||||||
}
|
|
||||||
if (Input.GetKeyDown(KeyCode.End))
|
|
||||||
{
|
scrollRect.verticalNormalizedPosition = Math.Min(1f, scrollRect.verticalNormalizedPosition + pageSize);
|
||||||
lightScroller.SetScrollPosition(1f);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.RebindPageUpDown.Value)
|
if (Input.GetKeyDown(KeyCode.PageDown))
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(KeyCode.PageUp))
|
// Duplicate this code to avoid running it every frame
|
||||||
{
|
Rect contentRect = scrollRect.content.rect;
|
||||||
var eventData = new PointerEventData(EventSystem.current)
|
Rect viewRect = scrollRect.RectTransform().rect;
|
||||||
{
|
float pageSize = viewRect.height / contentRect.height;
|
||||||
scrollDelta = new Vector2(0f, 25f)
|
|
||||||
};
|
|
||||||
lightScroller.OnScroll(eventData);
|
scrollRect.verticalNormalizedPosition = Math.Max(0f, scrollRect.verticalNormalizedPosition - pageSize);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (Input.GetKeyDown(KeyCode.PageDown))
|
}
|
||||||
{
|
}
|
||||||
var eventData = new PointerEventData(EventSystem.current)
|
|
||||||
{
|
return false;
|
||||||
scrollDelta = new Vector2(0f, -25f)
|
}
|
||||||
};
|
|
||||||
lightScroller.OnScroll(eventData);
|
// LightScrollers don't expose heights that I can see, so just fudge it with fake OnScroll events
|
||||||
return true;
|
private static bool HandleInput(LightScroller lightScroller)
|
||||||
}
|
{
|
||||||
|
if (lightScroller != null)
|
||||||
|
{
|
||||||
|
if (Settings.UseHomeEnd.Value)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.Home))
|
||||||
|
{
|
||||||
|
lightScroller.SetScrollPosition(0f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Input.GetKeyDown(KeyCode.End))
|
||||||
|
{
|
||||||
|
lightScroller.SetScrollPosition(1f);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (Settings.RebindPageUpDown.Value)
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<CodeInstruction> RemovePageUpDownHandling(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
foreach (var instruction in instructions)
|
|
||||||
{
|
{
|
||||||
if (instruction.LoadsConstant((int)KeyCode.PageUp))
|
if (Input.GetKeyDown(KeyCode.PageUp))
|
||||||
{
|
{
|
||||||
yield return new CodeInstruction(instruction)
|
var eventData = new PointerEventData(EventSystem.current)
|
||||||
{
|
{
|
||||||
operand = 0
|
scrollDelta = new Vector2(0f, 25f)
|
||||||
};
|
};
|
||||||
|
lightScroller.OnScroll(eventData);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (instruction.LoadsConstant((int)KeyCode.PageDown))
|
if (Input.GetKeyDown(KeyCode.PageDown))
|
||||||
{
|
{
|
||||||
yield return new CodeInstruction(instruction)
|
var eventData = new PointerEventData(EventSystem.current)
|
||||||
{
|
{
|
||||||
operand = 0
|
scrollDelta = new Vector2(0f, -25f)
|
||||||
};
|
};
|
||||||
|
lightScroller.OnScroll(eventData);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<CodeInstruction> RemovePageUpDownHandling(IEnumerable<CodeInstruction> instructions)
|
||||||
|
{
|
||||||
|
foreach (var instruction in instructions)
|
||||||
|
{
|
||||||
|
if (instruction.LoadsConstant((int)KeyCode.PageUp))
|
||||||
|
{
|
||||||
|
yield return new CodeInstruction(instruction)
|
||||||
{
|
{
|
||||||
yield return instruction;
|
operand = 0
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
else if (instruction.LoadsConstant((int)KeyCode.PageDown))
|
||||||
|
|
||||||
public class KeyScrollListener : MonoBehaviour
|
|
||||||
{
|
|
||||||
private ScrollRect scrollRect;
|
|
||||||
|
|
||||||
public UnityEvent OnKeyScroll;
|
|
||||||
|
|
||||||
public void Awake()
|
|
||||||
{
|
{
|
||||||
scrollRect = GetComponent<ScrollRect>();
|
yield return new CodeInstruction(instruction)
|
||||||
OnKeyScroll = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (HandleInput(scrollRect))
|
|
||||||
{
|
{
|
||||||
OnKeyScroll.Invoke();
|
operand = 0
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yield return instruction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class EnhanceStashScrollingPatch : ModulePatch
|
public class KeyScrollListener : MonoBehaviour
|
||||||
|
{
|
||||||
|
private ScrollRect scrollRect;
|
||||||
|
|
||||||
|
public UnityEvent OnKeyScroll;
|
||||||
|
|
||||||
|
public void Awake()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
scrollRect = GetComponent<ScrollRect>();
|
||||||
{
|
OnKeyScroll = new();
|
||||||
return AccessTools.Method(typeof(SimpleStashPanel), nameof(SimpleStashPanel.Update));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(SimpleStashPanel __instance, ScrollRect ____stashScroll)
|
|
||||||
{
|
|
||||||
// For some reason, sometimes SimpleStashPanel doesn't have a reference to its own ScrollRect?
|
|
||||||
HandleInput(____stashScroll ?? __instance.GetComponentInChildren<ScrollRect>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchTranspiler]
|
|
||||||
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
if (Settings.RebindPageUpDown.Value)
|
|
||||||
{
|
|
||||||
return RemovePageUpDownHandling(instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instructions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EnchanceTraderStashScrollingPatch : ModulePatch
|
public void Update()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (HandleInput(scrollRect))
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(TraderDealScreen), nameof(TraderDealScreen.Update));
|
OnKeyScroll.Invoke();
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(TraderDealScreen.ETraderMode ___etraderMode_0, ScrollRect ____traderScroll, ScrollRect ____stashScroll)
|
|
||||||
{
|
|
||||||
HandleInput(___etraderMode_0 == TraderDealScreen.ETraderMode.Purchase ? ____traderScroll : ____stashScroll);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchTranspiler]
|
|
||||||
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
if (Settings.RebindPageUpDown.Value)
|
|
||||||
{
|
|
||||||
return RemovePageUpDownHandling(instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instructions;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class EnhanceFleaScrollingPatch : ModulePatch
|
public class EnhanceStashScrollingPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(SimpleStashPanel), nameof(SimpleStashPanel.Update));
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.Update));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(LightScroller ____scroller)
|
|
||||||
{
|
|
||||||
HandleInput(____scroller);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchTranspiler]
|
|
||||||
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
|
||||||
{
|
|
||||||
if (Settings.RebindPageUpDown.Value)
|
|
||||||
{
|
|
||||||
return RemovePageUpDownHandling(instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instructions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EnhanceHideoutScrollingPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix(SimpleStashPanel __instance, ScrollRect ____stashScroll)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
// For some reason, sometimes SimpleStashPanel doesn't have a reference to its own ScrollRect?
|
||||||
{
|
HandleInput(____stashScroll ?? __instance.GetComponentInChildren<ScrollRect>());
|
||||||
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.Awake));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(AreaScreenSubstrate __instance)
|
|
||||||
{
|
|
||||||
ScrollRect scrollRect = __instance.transform.Find("Content/CurrentLevel/CurrentContainer/Scrollview")?.GetComponent<ScrollRect>();
|
|
||||||
if (scrollRect == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollRect.GetOrAddComponent<KeyScrollListener>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EnhanceMailScrollingPatch : ModulePatch
|
[PatchTranspiler]
|
||||||
|
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (Settings.RebindPageUpDown.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(MessagesContainer), nameof(MessagesContainer.Update));
|
return RemovePageUpDownHandling(instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
return instructions;
|
||||||
public static void Prefix(LightScroller ____scroller)
|
}
|
||||||
{
|
}
|
||||||
HandleInput(____scroller);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchTranspiler]
|
public class EnchanceTraderStashScrollingPatch : ModulePatch
|
||||||
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
if (Settings.RebindPageUpDown.Value)
|
{
|
||||||
{
|
return AccessTools.Method(typeof(TraderDealScreen), nameof(TraderDealScreen.Update));
|
||||||
return RemovePageUpDownHandling(instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instructions;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MouseScrollingSpeedPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix(TraderDealScreen.ETraderMode ___etraderMode_0, ScrollRect ____traderScroll, ScrollRect ____stashScroll)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
HandleInput(___etraderMode_0 == TraderDealScreen.ETraderMode.Purchase ? ____traderScroll : ____stashScroll);
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ScrollRectNoDrag), nameof(ScrollRectNoDrag.OnScroll));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(PointerEventData data)
|
|
||||||
{
|
|
||||||
int multi = Settings.UseRaidMouseScrollMulti.Value && Plugin.InRaid() ? Settings.MouseScrollMultiInRaid.Value : Settings.MouseScrollMulti.Value;
|
|
||||||
data.scrollDelta *= multi;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EnhanceTaskListScrollingPatch : ModulePatch
|
[PatchTranspiler]
|
||||||
|
public static IEnumerable<CodeInstruction> Transpile(IEnumerable<CodeInstruction> instructions)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (Settings.RebindPageUpDown.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(TasksScreen), nameof(TasksScreen.Awake));
|
return RemovePageUpDownHandling(instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
return instructions;
|
||||||
public static void Postfix(ScrollRect ____scrollRect)
|
}
|
||||||
{
|
}
|
||||||
var keyScroller = ____scrollRect.GetOrAddComponent<KeyScroller>();
|
|
||||||
keyScroller.Init(____scrollRect);
|
public class EnhanceFleaScrollingPatch : ModulePatch
|
||||||
}
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(OfferViewList), nameof(OfferViewList.Update));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class KeyScroller : MonoBehaviour
|
[PatchPrefix]
|
||||||
|
public static void Prefix(LightScroller ____scroller)
|
||||||
{
|
{
|
||||||
ScrollRect scrollRect;
|
HandleInput(____scroller);
|
||||||
|
|
||||||
public void Init(ScrollRect scrollRect)
|
|
||||||
{
|
|
||||||
this.scrollRect = scrollRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
HandleInput(scrollRect);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OpenLastTaskPatch : ModulePatch
|
[PatchTranspiler]
|
||||||
|
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||||
{
|
{
|
||||||
private static string LastQuestId = null;
|
if (Settings.RebindPageUpDown.Value)
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(NotesTask), nameof(NotesTask.Show));
|
return RemovePageUpDownHandling(instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
return instructions;
|
||||||
public static void Postfix(NotesTask __instance, QuestClass quest)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EnhanceHideoutScrollingPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AreaScreenSubstrate), nameof(AreaScreenSubstrate.Awake));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(AreaScreenSubstrate __instance)
|
||||||
|
{
|
||||||
|
ScrollRect scrollRect = __instance.transform.Find("Content/CurrentLevel/CurrentContainer/Scrollview")?.GetComponent<ScrollRect>();
|
||||||
|
if (scrollRect == null)
|
||||||
{
|
{
|
||||||
void OnTaskSelected(bool open)
|
return;
|
||||||
{
|
}
|
||||||
LastQuestId = open ? quest.Id : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toggle toggle = __instance.GetComponent<Toggle>();
|
scrollRect.GetOrAddComponent<KeyScrollListener>();
|
||||||
toggle.onValueChanged.AddListener(OnTaskSelected);
|
}
|
||||||
__instance.R().UI.AddDisposable(() => toggle.onValueChanged.RemoveListener(OnTaskSelected));
|
}
|
||||||
|
|
||||||
if (quest.Id == LastQuestId)
|
public class EnhanceMailScrollingPatch : ModulePatch
|
||||||
{
|
{
|
||||||
toggle.isOn = true;
|
protected override MethodBase GetTargetMethod()
|
||||||
}
|
{
|
||||||
|
return AccessTools.Method(typeof(MessagesContainer), nameof(MessagesContainer.Update));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(LightScroller ____scroller)
|
||||||
|
{
|
||||||
|
HandleInput(____scroller);
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchTranspiler]
|
||||||
|
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
|
||||||
|
{
|
||||||
|
if (Settings.RebindPageUpDown.Value)
|
||||||
|
{
|
||||||
|
return RemovePageUpDownHandling(instructions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MouseScrollingSpeedPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(ScrollRectNoDrag), nameof(ScrollRectNoDrag.OnScroll));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static void Prefix(PointerEventData data)
|
||||||
|
{
|
||||||
|
int multi = Settings.UseRaidMouseScrollMulti.Value && Plugin.InRaid() ? Settings.MouseScrollMultiInRaid.Value : Settings.MouseScrollMulti.Value;
|
||||||
|
data.scrollDelta *= multi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EnhanceTaskListScrollingPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(TasksScreen), nameof(TasksScreen.Awake));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ScrollRect ____scrollRect)
|
||||||
|
{
|
||||||
|
var keyScroller = ____scrollRect.GetOrAddComponent<KeyScroller>();
|
||||||
|
keyScroller.Init(____scrollRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KeyScroller : MonoBehaviour
|
||||||
|
{
|
||||||
|
ScrollRect scrollRect;
|
||||||
|
|
||||||
|
public void Init(ScrollRect scrollRect)
|
||||||
|
{
|
||||||
|
this.scrollRect = scrollRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
HandleInput(scrollRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OpenLastTaskPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static string LastQuestId = null;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(NotesTask), nameof(NotesTask.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(NotesTask __instance, QuestClass quest)
|
||||||
|
{
|
||||||
|
void OnTaskSelected(bool open)
|
||||||
|
{
|
||||||
|
LastQuestId = open ? quest.Id : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toggle toggle = __instance.GetComponent<Toggle>();
|
||||||
|
toggle.onValueChanged.AddListener(OnTaskSelected);
|
||||||
|
__instance.R().UI.AddDisposable(() => toggle.onValueChanged.RemoveListener(OnTaskSelected));
|
||||||
|
|
||||||
|
if (quest.Id == LastQuestId)
|
||||||
|
{
|
||||||
|
toggle.isOn = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,96 +6,95 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class StackFirItemsPatches
|
||||||
{
|
{
|
||||||
public static class StackFirItemsPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new ContainerStackPatch().Enable();
|
||||||
|
new TopUpStackPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContainerStackPatch : ModulePatch
|
||||||
|
{
|
||||||
|
private static Type MergeableItemType;
|
||||||
|
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new ContainerStackPatch().Enable();
|
MethodInfo method = AccessTools.Method(typeof(InteractionsHandlerClass), nameof(InteractionsHandlerClass.smethod_0));
|
||||||
new TopUpStackPatch().Enable();
|
MergeableItemType = method.GetParameters()[2].ParameterType.GetElementType(); // parameter is a ref type, get underlying type, GClass2751
|
||||||
|
return method;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContainerStackPatch : ModulePatch
|
// Reimplementing this entire method to ignore SpawnedInSession for certain types
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(IEnumerable<EFT.InventoryLogic.IContainer> containersToPut, Item itemToMerge, ref object mergeableItem, int overrideCount, ref bool __result)
|
||||||
{
|
{
|
||||||
private static Type MergeableItemType;
|
if (!MergeableItemType.IsInstanceOfType(itemToMerge))
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
{
|
||||||
MethodInfo method = AccessTools.Method(typeof(InteractionsHandlerClass), nameof(InteractionsHandlerClass.smethod_0));
|
mergeableItem = null;
|
||||||
MergeableItemType = method.GetParameters()[2].ParameterType.GetElementType(); // parameter is a ref type, get underlying type, GClass2751
|
__result = false;
|
||||||
return method;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reimplementing this entire method to ignore SpawnedInSession for certain types
|
if (overrideCount <= 0)
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(IEnumerable<EFT.InventoryLogic.IContainer> containersToPut, Item itemToMerge, ref object mergeableItem, int overrideCount, ref bool __result)
|
|
||||||
{
|
{
|
||||||
if (!MergeableItemType.IsInstanceOfType(itemToMerge))
|
overrideCount = itemToMerge.StackObjectsCount;
|
||||||
{
|
|
||||||
mergeableItem = null;
|
|
||||||
__result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overrideCount <= 0)
|
|
||||||
{
|
|
||||||
overrideCount = itemToMerge.StackObjectsCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ignoreSpawnedInSession;
|
|
||||||
if (itemToMerge.Template is MoneyClass)
|
|
||||||
{
|
|
||||||
ignoreSpawnedInSession = Settings.MergeFIRMoney.Value;
|
|
||||||
}
|
|
||||||
else if (itemToMerge.Template is AmmoTemplate)
|
|
||||||
{
|
|
||||||
ignoreSpawnedInSession = Settings.MergeFIRAmmo.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ignoreSpawnedInSession = Settings.MergeFIROther.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeableItem = containersToPut.SelectMany(x => x.Items)
|
|
||||||
.Where(MergeableItemType.IsInstanceOfType)
|
|
||||||
.Where(x => x != itemToMerge)
|
|
||||||
.Where(x => x.TemplateId == itemToMerge.TemplateId)
|
|
||||||
.Where(x => ignoreSpawnedInSession || x.SpawnedInSession == itemToMerge.SpawnedInSession)
|
|
||||||
.Where(x => x.StackObjectsCount < x.StackMaxSize)
|
|
||||||
.FirstOrDefault(x => overrideCount <= x.StackMaxSize - x.StackObjectsCount);
|
|
||||||
|
|
||||||
__result = mergeableItem != null;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ignoreSpawnedInSession;
|
||||||
|
if (itemToMerge.Template is MoneyClass)
|
||||||
|
{
|
||||||
|
ignoreSpawnedInSession = Settings.MergeFIRMoney.Value;
|
||||||
|
}
|
||||||
|
else if (itemToMerge.Template is AmmoTemplate)
|
||||||
|
{
|
||||||
|
ignoreSpawnedInSession = Settings.MergeFIRAmmo.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ignoreSpawnedInSession = Settings.MergeFIROther.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
mergeableItem = containersToPut.SelectMany(x => x.Items)
|
||||||
|
.Where(MergeableItemType.IsInstanceOfType)
|
||||||
|
.Where(x => x != itemToMerge)
|
||||||
|
.Where(x => x.TemplateId == itemToMerge.TemplateId)
|
||||||
|
.Where(x => ignoreSpawnedInSession || x.SpawnedInSession == itemToMerge.SpawnedInSession)
|
||||||
|
.Where(x => x.StackObjectsCount < x.StackMaxSize)
|
||||||
|
.FirstOrDefault(x => overrideCount <= x.StackMaxSize - x.StackObjectsCount);
|
||||||
|
|
||||||
|
__result = mergeableItem != null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TopUpStackPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(Item), nameof(Item.IsSameItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TopUpStackPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(Item __instance, Item other, ref bool __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
bool ignoreSpawnedInSession;
|
||||||
|
if (__instance.Template is MoneyClass)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(Item), nameof(Item.IsSameItem));
|
ignoreSpawnedInSession = Settings.MergeFIRMoney.Value;
|
||||||
|
}
|
||||||
|
else if (__instance.Template is AmmoTemplate)
|
||||||
|
{
|
||||||
|
ignoreSpawnedInSession = Settings.MergeFIRAmmo.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ignoreSpawnedInSession = Settings.MergeFIROther.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
__result = __instance.TemplateId == other.TemplateId && __instance.Id != other.Id && (ignoreSpawnedInSession || __instance.SpawnedInSession == other.SpawnedInSession);
|
||||||
public static bool Prefix(Item __instance, Item other, ref bool __result)
|
return false;
|
||||||
{
|
|
||||||
bool ignoreSpawnedInSession;
|
|
||||||
if (__instance.Template is MoneyClass)
|
|
||||||
{
|
|
||||||
ignoreSpawnedInSession = Settings.MergeFIRMoney.Value;
|
|
||||||
}
|
|
||||||
else if (__instance.Template is AmmoTemplate)
|
|
||||||
{
|
|
||||||
ignoreSpawnedInSession = Settings.MergeFIRAmmo.Value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ignoreSpawnedInSession = Settings.MergeFIROther.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
__result = __instance.TemplateId == other.TemplateId && __instance.Id != other.Id && (ignoreSpawnedInSession || __instance.SpawnedInSession == other.SpawnedInSession);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,84 +6,83 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class StackMoveGreedyPatches
|
||||||
{
|
{
|
||||||
public static class StackMoveGreedyPatches
|
private static bool InPatch = false;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static bool InPatch = false;
|
new GridViewPatch().Enable();
|
||||||
|
new SlotViewPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
public class GridViewPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new GridViewPatch().Enable();
|
return AccessTools.Method(typeof(GridView), nameof(GridView.AcceptItem));
|
||||||
new SlotViewPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GridViewPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
[HarmonyPriority(Priority.LowerThanNormal)]
|
||||||
|
public static bool Prefix(GridView __instance, DragItemContext itemContext, ItemContextAbstractClass targetItemContext, ref Task __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AcceptStackable(__instance, itemContext, targetItemContext, ref __result);
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(GridView), nameof(GridView.AcceptItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
[HarmonyPriority(Priority.LowerThanNormal)]
|
|
||||||
public static bool Prefix(GridView __instance, DragItemContext itemContext, ItemContextAbstractClass targetItemContext, ref Task __result)
|
|
||||||
{
|
|
||||||
return AcceptStackable(__instance, itemContext, targetItemContext, ref __result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SlotViewPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(SlotView), nameof(SlotView.AcceptItem));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
[HarmonyPriority(Priority.LowerThanNormal)]
|
|
||||||
public static bool Prefix(SlotView __instance, DragItemContext itemContext, ItemContextAbstractClass targetItemContext, ref Task __result)
|
|
||||||
{
|
|
||||||
return AcceptStackable(__instance, itemContext, targetItemContext, ref __result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specific type of TaskSerializer because Unity can't understand generics
|
|
||||||
public class ItemContextTaskSerializer : TaskSerializer<DragItemContext> { }
|
|
||||||
|
|
||||||
private static bool AcceptStackable<T>(T __instance, DragItemContext itemContext, ItemContextAbstractClass targetItemContext, ref Task __result) where T : MonoBehaviour, IContainer
|
|
||||||
{
|
|
||||||
if (!Settings.GreedyStackMove.Value || InPatch || itemContext.Item.StackObjectsCount <= 1 || targetItemContext == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
InPatch = true;
|
|
||||||
|
|
||||||
int stackCount = int.MaxValue;
|
|
||||||
var serializer = __instance.gameObject.AddComponent<ItemContextTaskSerializer>();
|
|
||||||
__result = serializer.Initialize(itemContext.RepeatUntilEmpty(), ic =>
|
|
||||||
{
|
|
||||||
if (ic.Item.StackObjectsCount >= stackCount)
|
|
||||||
{
|
|
||||||
// Nothing happened, bail out
|
|
||||||
return Task.FromCanceled(new CancellationToken(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
stackCount = ic.Item.StackObjectsCount;
|
|
||||||
return __instance.AcceptItem(ic, targetItemContext);
|
|
||||||
});
|
|
||||||
|
|
||||||
// This won't block the first action from swapping, but will prevent follow up swaps
|
|
||||||
SwapPatches.BlockSwaps = true;
|
|
||||||
|
|
||||||
__result.ContinueWith(_ =>
|
|
||||||
{
|
|
||||||
InPatch = false;
|
|
||||||
SwapPatches.BlockSwaps = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SlotViewPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(SlotView), nameof(SlotView.AcceptItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
[HarmonyPriority(Priority.LowerThanNormal)]
|
||||||
|
public static bool Prefix(SlotView __instance, DragItemContext itemContext, ItemContextAbstractClass targetItemContext, ref Task __result)
|
||||||
|
{
|
||||||
|
return AcceptStackable(__instance, itemContext, targetItemContext, ref __result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific type of TaskSerializer because Unity can't understand generics
|
||||||
|
public class ItemContextTaskSerializer : TaskSerializer<DragItemContext> { }
|
||||||
|
|
||||||
|
private static bool AcceptStackable<T>(T __instance, DragItemContext itemContext, ItemContextAbstractClass targetItemContext, ref Task __result) where T : MonoBehaviour, IContainer
|
||||||
|
{
|
||||||
|
if (!Settings.GreedyStackMove.Value || InPatch || itemContext.Item.StackObjectsCount <= 1 || targetItemContext == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InPatch = true;
|
||||||
|
|
||||||
|
int stackCount = int.MaxValue;
|
||||||
|
var serializer = __instance.gameObject.AddComponent<ItemContextTaskSerializer>();
|
||||||
|
__result = serializer.Initialize(itemContext.RepeatUntilEmpty(), ic =>
|
||||||
|
{
|
||||||
|
if (ic.Item.StackObjectsCount >= stackCount)
|
||||||
|
{
|
||||||
|
// Nothing happened, bail out
|
||||||
|
return Task.FromCanceled(new CancellationToken(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
stackCount = ic.Item.StackObjectsCount;
|
||||||
|
return __instance.AcceptItem(ic, targetItemContext);
|
||||||
|
});
|
||||||
|
|
||||||
|
// This won't block the first action from swapping, but will prevent follow up swaps
|
||||||
|
SwapPatches.BlockSwaps = true;
|
||||||
|
|
||||||
|
__result.ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
InPatch = false;
|
||||||
|
SwapPatches.BlockSwaps = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,83 +6,82 @@ using System.Reflection;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class SyncScrollPositionPatches
|
||||||
{
|
{
|
||||||
public static class SyncScrollPositionPatches
|
private static float StashScrollPosition = 1f;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static float StashScrollPosition = 1f;
|
new SyncStashScrollPatch().Enable();
|
||||||
|
new SyncTraderStashScrollPatch().Enable();
|
||||||
|
new SyncOfferStashScrollPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
private static void UpdateScrollPosition(Vector2 position)
|
||||||
|
{
|
||||||
|
StashScrollPosition = position.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SynchronizeScrollRect(UIElement element, ScrollRect scrollRect = null)
|
||||||
|
{
|
||||||
|
if (!Settings.SynchronizeStashScrolling.Value || element == null || (scrollRect ??= element.GetComponentInChildren<ScrollRect>()) == null)
|
||||||
{
|
{
|
||||||
new SyncStashScrollPatch().Enable();
|
return;
|
||||||
new SyncTraderStashScrollPatch().Enable();
|
|
||||||
new SyncOfferStashScrollPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateScrollPosition(Vector2 position)
|
scrollRect.verticalNormalizedPosition = StashScrollPosition;
|
||||||
|
|
||||||
|
scrollRect.onValueChanged.RemoveListener(UpdateScrollPosition);
|
||||||
|
scrollRect.onValueChanged.AddListener(UpdateScrollPosition);
|
||||||
|
//element.R().UI.AddDisposable(() => scrollRect.onValueChanged.RemoveListener(UpdateScrollPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SyncStashScrollPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
StashScrollPosition = position.y;
|
return AccessTools.Method(typeof(SimpleStashPanel), nameof(SimpleStashPanel.Show));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SynchronizeScrollRect(UIElement element, ScrollRect scrollRect = null)
|
[PatchPostfix]
|
||||||
|
public static void Postfix(SimpleStashPanel __instance)
|
||||||
{
|
{
|
||||||
if (!Settings.SynchronizeStashScrolling.Value || element == null || (scrollRect ??= element.GetComponentInChildren<ScrollRect>()) == null)
|
SynchronizeScrollRect(__instance);
|
||||||
{
|
}
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
scrollRect.verticalNormalizedPosition = StashScrollPosition;
|
public class SyncTraderStashScrollPatch : ModulePatch
|
||||||
|
{
|
||||||
scrollRect.onValueChanged.RemoveListener(UpdateScrollPosition);
|
protected override MethodBase GetTargetMethod()
|
||||||
scrollRect.onValueChanged.AddListener(UpdateScrollPosition);
|
{
|
||||||
//element.R().UI.AddDisposable(() => scrollRect.onValueChanged.RemoveListener(UpdateScrollPosition));
|
return AccessTools.Method(typeof(TraderDealScreen), nameof(TraderDealScreen.method_3));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SyncStashScrollPatch : ModulePatch
|
// TraderDealScreen is a monstrosity that loads multiple times and isn't done loading when Show() is done
|
||||||
|
// method_3 shows the stash grid, if method_5() returned true
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(TraderDealScreen __instance, ScrollRect ____stashScroll)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (__instance.method_5())
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(SimpleStashPanel), nameof(SimpleStashPanel.Show));
|
SynchronizeScrollRect(__instance, ____stashScroll);
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(SimpleStashPanel __instance)
|
|
||||||
{
|
|
||||||
SynchronizeScrollRect(__instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SyncTraderStashScrollPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(TraderDealScreen), nameof(TraderDealScreen.method_3));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TraderDealScreen is a monstrosity that loads multiple times and isn't done loading when Show() is done
|
|
||||||
// method_3 shows the stash grid, if method_5() returned true
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(TraderDealScreen __instance, ScrollRect ____stashScroll)
|
|
||||||
{
|
|
||||||
if (__instance.method_5())
|
|
||||||
{
|
|
||||||
SynchronizeScrollRect(__instance, ____stashScroll);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SyncOfferStashScrollPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void Postfix(AddOfferWindow __instance)
|
|
||||||
{
|
|
||||||
SynchronizeScrollRect(__instance);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SyncOfferStashScrollPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(AddOfferWindow), nameof(AddOfferWindow.Show));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPostfix]
|
||||||
|
public static void Postfix(AddOfferWindow __instance)
|
||||||
|
{
|
||||||
|
SynchronizeScrollRect(__instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,99 +7,98 @@ using System.Reflection;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class TradingAutoSwitchPatches
|
||||||
{
|
{
|
||||||
public static class TradingAutoSwitchPatches
|
private static Tab BuyTab;
|
||||||
|
private static Tab SellTab;
|
||||||
|
|
||||||
|
public static void Enable()
|
||||||
{
|
{
|
||||||
private static Tab BuyTab;
|
new GetTraderScreensGroupPatch().Enable();
|
||||||
private static Tab SellTab;
|
new SwitchOnClickPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
public class GetTraderScreensGroupPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new GetTraderScreensGroupPatch().Enable();
|
return AccessTools.Method(typeof(TraderScreensGroup), nameof(TraderScreensGroup.Show));
|
||||||
new SwitchOnClickPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GetTraderScreensGroupPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(TraderScreensGroup __instance)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var wrappedInstance = __instance.R();
|
||||||
|
|
||||||
|
BuyTab = wrappedInstance.BuyTab;
|
||||||
|
SellTab = wrappedInstance.SellTab;
|
||||||
|
|
||||||
|
wrappedInstance.UI.AddDisposable(() =>
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(TraderScreensGroup), nameof(TraderScreensGroup.Show));
|
BuyTab = null;
|
||||||
}
|
SellTab = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
public class SwitchOnClickPatch : ModulePatch
|
||||||
public static void Postfix(TraderScreensGroup __instance)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
var wrappedInstance = __instance.R();
|
{
|
||||||
|
return AccessTools.Method(typeof(TradingItemView), nameof(TradingItemView.OnClick));
|
||||||
BuyTab = wrappedInstance.BuyTab;
|
|
||||||
SellTab = wrappedInstance.SellTab;
|
|
||||||
|
|
||||||
wrappedInstance.UI.AddDisposable(() =>
|
|
||||||
{
|
|
||||||
BuyTab = null;
|
|
||||||
SellTab = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SwitchOnClickPatch : ModulePatch
|
// Basically reimplementing this method for the two cases I want to handle
|
||||||
|
// Key difference being NOT to check the current trading mode, and to call switch at the end
|
||||||
|
// Have to call switch *after*, because it completely rebuilds the entire player-side grid
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(
|
||||||
|
TradingItemView __instance,
|
||||||
|
PointerEventData.InputButton button,
|
||||||
|
bool doubleClick,
|
||||||
|
ETradingItemViewType ___etradingItemViewType_0, bool ___bool_8)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (!Settings.AutoSwitchTrading.Value)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(TradingItemView), nameof(TradingItemView.OnClick));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basically reimplementing this method for the two cases I want to handle
|
|
||||||
// Key difference being NOT to check the current trading mode, and to call switch at the end
|
|
||||||
// Have to call switch *after*, because it completely rebuilds the entire player-side grid
|
|
||||||
[PatchPrefix]
|
|
||||||
public static bool Prefix(
|
|
||||||
TradingItemView __instance,
|
|
||||||
PointerEventData.InputButton button,
|
|
||||||
bool doubleClick,
|
|
||||||
ETradingItemViewType ___etradingItemViewType_0, bool ___bool_8)
|
|
||||||
{
|
|
||||||
if (!Settings.AutoSwitchTrading.Value)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var tradingItemView = __instance.R();
|
|
||||||
if (button != PointerEventData.InputButton.Left || ___etradingItemViewType_0 == ETradingItemViewType.TradingTable)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ctrlPressed = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
|
|
||||||
|
|
||||||
if (!ctrlPressed && doubleClick)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!___bool_8 && ctrlPressed && tradingItemView.TraderAssortmentController.QuickFindTradingAppropriatePlace(__instance.Item, null))
|
|
||||||
{
|
|
||||||
__instance.ItemContext.CloseDependentWindows();
|
|
||||||
__instance.HideTooltip();
|
|
||||||
Singleton<GUISounds>.Instance.PlayItemSound(__instance.Item.ItemSound, EInventorySoundType.pickup, false);
|
|
||||||
|
|
||||||
SellTab.OnPointerClick(null);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (___bool_8)
|
|
||||||
{
|
|
||||||
tradingItemView.TraderAssortmentController.SelectItem(__instance.Item);
|
|
||||||
|
|
||||||
BuyTab.OnPointerClick(null);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var tradingItemView = __instance.R();
|
||||||
|
if (button != PointerEventData.InputButton.Left || ___etradingItemViewType_0 == ETradingItemViewType.TradingTable)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ctrlPressed = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
|
||||||
|
|
||||||
|
if (!ctrlPressed && doubleClick)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!___bool_8 && ctrlPressed && tradingItemView.TraderAssortmentController.QuickFindTradingAppropriatePlace(__instance.Item, null))
|
||||||
|
{
|
||||||
|
__instance.ItemContext.CloseDependentWindows();
|
||||||
|
__instance.HideTooltip();
|
||||||
|
Singleton<GUISounds>.Instance.PlayItemSound(__instance.Item.ItemSound, EInventorySoundType.pickup, false);
|
||||||
|
|
||||||
|
SellTab.OnPointerClick(null);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (___bool_8)
|
||||||
|
{
|
||||||
|
tradingItemView.TraderAssortmentController.SelectItem(__instance.Item);
|
||||||
|
|
||||||
|
BuyTab.OnPointerClick(null);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,26 +4,25 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class TransferConfirmPatch : ModulePatch
|
||||||
{
|
{
|
||||||
public class TransferConfirmPatch : ModulePatch
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
return AccessTools.Method(typeof(TransferItemsScreen), nameof(TransferItemsScreen.method_4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(ref Task<bool> __result)
|
||||||
|
{
|
||||||
|
if (Settings.ShowTransferConfirmations.Value == TransferConfirmationOption.Always)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(TransferItemsScreen), nameof(TransferItemsScreen.method_4));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
__result = Task.FromResult(true);
|
||||||
public static bool Prefix(ref Task<bool> __result)
|
return false;
|
||||||
{
|
|
||||||
if (Settings.ShowTransferConfirmations.Value == TransferConfirmationOption.Always)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
__result = Task.FromResult(true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,94 +9,93 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class UnloadAmmoPatches
|
||||||
{
|
{
|
||||||
public static class UnloadAmmoPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new TradingPlayerPatch().Enable();
|
||||||
|
new TransferPlayerPatch().Enable();
|
||||||
|
new UnloadScavTransferPatch().Enable();
|
||||||
|
new NoScavStashPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TradingPlayerPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new TradingPlayerPatch().Enable();
|
return AccessTools.DeclaredProperty(R.TradingInteractions.Type, "AvailableInteractions").GetMethod;
|
||||||
new TransferPlayerPatch().Enable();
|
|
||||||
new UnloadScavTransferPatch().Enable();
|
|
||||||
new NoScavStashPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TradingPlayerPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var list = __result.ToList();
|
||||||
{
|
list.Insert(list.IndexOf(EItemInfoButton.Repair), EItemInfoButton.UnloadAmmo);
|
||||||
return AccessTools.DeclaredProperty(R.TradingInteractions.Type, "AvailableInteractions").GetMethod;
|
__result = list;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
public class TransferPlayerPatch : ModulePatch
|
||||||
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
var list = __result.ToList();
|
{
|
||||||
list.Insert(list.IndexOf(EItemInfoButton.Repair), EItemInfoButton.UnloadAmmo);
|
return AccessTools.DeclaredProperty(R.TransferInteractions.Type, "AvailableInteractions").GetMethod;
|
||||||
__result = list;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TransferPlayerPatch : ModulePatch
|
[PatchPostfix]
|
||||||
|
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var list = __result.ToList();
|
||||||
{
|
list.Insert(list.IndexOf(EItemInfoButton.Fold), EItemInfoButton.UnloadAmmo);
|
||||||
return AccessTools.DeclaredProperty(R.TransferInteractions.Type, "AvailableInteractions").GetMethod;
|
__result = list;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[PatchPostfix]
|
// The scav inventory screen has two inventory controllers, the player's and the scav's. Unload always uses the player's, which causes issues
|
||||||
public static void Postfix(ref IEnumerable<EItemInfoButton> __result)
|
// because the bullets are never marked as "known" by the scav, so if you click back/next they show up as unsearched, with no way to search
|
||||||
{
|
// This patch forces unload to use the controller of whoever owns the magazine.
|
||||||
var list = __result.ToList();
|
public class UnloadScavTransferPatch : ModulePatch
|
||||||
list.Insert(list.IndexOf(EItemInfoButton.Fold), EItemInfoButton.UnloadAmmo);
|
{
|
||||||
__result = list;
|
protected override MethodBase GetTargetMethod()
|
||||||
}
|
{
|
||||||
|
return AccessTools.DeclaredMethod(typeof(InventoryControllerClass), nameof(InventoryControllerClass.UnloadMagazine));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The scav inventory screen has two inventory controllers, the player's and the scav's. Unload always uses the player's, which causes issues
|
[PatchPrefix]
|
||||||
// because the bullets are never marked as "known" by the scav, so if you click back/next they show up as unsearched, with no way to search
|
public static bool Prefix(InventoryControllerClass __instance, MagazineClass magazine, ref Task<IResult> __result)
|
||||||
// This patch forces unload to use the controller of whoever owns the magazine.
|
|
||||||
public class UnloadScavTransferPatch : ModulePatch
|
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (ItemUiContext.Instance.ContextType != EItemUiContextType.ScavengerInventoryScreen)
|
||||||
{
|
{
|
||||||
return AccessTools.DeclaredMethod(typeof(InventoryControllerClass), nameof(InventoryControllerClass.UnloadMagazine));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
if (magazine.Owner == __instance || magazine.Owner is not InventoryControllerClass ownerInventoryController)
|
||||||
public static bool Prefix(InventoryControllerClass __instance, MagazineClass magazine, ref Task<IResult> __result)
|
|
||||||
{
|
{
|
||||||
if (ItemUiContext.Instance.ContextType != EItemUiContextType.ScavengerInventoryScreen)
|
return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (magazine.Owner == __instance || magazine.Owner is not InventoryControllerClass ownerInventoryController)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
__result = ownerInventoryController.UnloadMagazine(magazine);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__result = ownerInventoryController.UnloadMagazine(magazine);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because of the above patch, unload uses the scav's inventory controller, which provides locations to unload ammo: equipment and stash. Why do scavs have a stash?
|
||||||
|
// If the equipment is full, the bullets would go to the scav stash, aka a black hole, and are never seen again.
|
||||||
|
// Remove the scav's stash
|
||||||
|
public class NoScavStashPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
Type type = typeof(ScavengerInventoryScreen).GetNestedTypes().Single(t => t.GetField("ScavController") != null); // ScavengerInventoryScreen.GClass3156
|
||||||
|
return AccessTools.GetDeclaredConstructors(type).Single();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because of the above patch, unload uses the scav's inventory controller, which provides locations to unload ammo: equipment and stash. Why do scavs have a stash?
|
[PatchPrefix]
|
||||||
// If the equipment is full, the bullets would go to the scav stash, aka a black hole, and are never seen again.
|
public static void Prefix(InventoryContainerClass scavController)
|
||||||
// Remove the scav's stash
|
|
||||||
public class NoScavStashPatch : ModulePatch
|
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
scavController.Inventory.Stash = null;
|
||||||
{
|
|
||||||
Type type = typeof(ScavengerInventoryScreen).GetNestedTypes().Single(t => t.GetField("ScavController") != null); // ScavengerInventoryScreen.GClass3156
|
|
||||||
return AccessTools.GetDeclaredConstructors(type).Single();
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(InventoryContainerClass scavController)
|
|
||||||
{
|
|
||||||
scavController.Inventory.Stash = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,59 +6,57 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
// Two patches are required for the edit preset screen - one to grab the value of moveForward from CloseScreenInterruption(), and one to use it.
|
||||||
|
// This is because BSG didn't think to pass the argument in to method_35
|
||||||
|
public static class WeaponPresetConfirmPatches
|
||||||
{
|
{
|
||||||
// Two patches are required for the edit preset screen - one to grab the value of moveForward from CloseScreenInterruption(), and one to use it.
|
public static bool MoveForward;
|
||||||
// This is because BSG didn't think to pass the argument in to method_35
|
|
||||||
public static class WeaponPresetConfirmPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static bool MoveForward;
|
new DetectWeaponPresetCloseTypePatch().Enable();
|
||||||
|
new ConfirmDiscardWeaponPresetChangesPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
public static void Enable()
|
// This patch just caches whether this navigation is a forward navigation, which determines if the preset is actually closing
|
||||||
|
public class DetectWeaponPresetCloseTypePatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new DetectWeaponPresetCloseTypePatch().Enable();
|
Type type = typeof(EditBuildScreen).GetNestedTypes().Single(x => x.GetMethod("CloseScreenInterruption") != null); // EditBuildScreen.GClass3151
|
||||||
new ConfirmDiscardWeaponPresetChangesPatch().Enable();
|
return AccessTools.Method(type, "CloseScreenInterruption");
|
||||||
}
|
}
|
||||||
|
|
||||||
// This patch just caches whether this navigation is a forward navigation, which determines if the preset is actually closing
|
[PatchPrefix]
|
||||||
public class DetectWeaponPresetCloseTypePatch : ModulePatch
|
public static void Prefix(bool moveForward)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
MoveForward = moveForward;
|
||||||
{
|
}
|
||||||
Type type = typeof(EditBuildScreen).GetNestedTypes().Single(x => x.GetMethod("CloseScreenInterruption") != null); // EditBuildScreen.GClass3151
|
}
|
||||||
return AccessTools.Method(type, "CloseScreenInterruption");
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
public class ConfirmDiscardWeaponPresetChangesPatch : ModulePatch
|
||||||
public static void Prefix(bool moveForward)
|
{
|
||||||
{
|
protected override MethodBase GetTargetMethod()
|
||||||
MoveForward = moveForward;
|
{
|
||||||
}
|
return AccessTools.Method(typeof(EditBuildScreen), nameof(EditBuildScreen.method_35));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfirmDiscardWeaponPresetChangesPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static bool Prefix(ref Task<bool> __result)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
if (MoveForward && Settings.ShowPresetConfirmations.Value == WeaponPresetConfirmationOption.Always)
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(EditBuildScreen), nameof(EditBuildScreen.method_35));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PatchPrefix]
|
if (!MoveForward && Settings.ShowPresetConfirmations.Value != WeaponPresetConfirmationOption.Never)
|
||||||
public static bool Prefix(ref Task<bool> __result)
|
|
||||||
{
|
{
|
||||||
if (MoveForward && Settings.ShowPresetConfirmations.Value == WeaponPresetConfirmationOption.Always)
|
return true;
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MoveForward && Settings.ShowPresetConfirmations.Value != WeaponPresetConfirmationOption.Never)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
__result = Task.FromResult(true);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__result = Task.FromResult(true);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,58 +5,57 @@ using SPT.Reflection.Patching;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public static class WeaponZoomPatches
|
||||||
{
|
{
|
||||||
public static class WeaponZoomPatches
|
public static void Enable()
|
||||||
{
|
{
|
||||||
public static void Enable()
|
new EditBuildScreenZoomPatch().Enable();
|
||||||
|
new WeaponModdingScreenZoomPatch().Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EditBuildScreenZoomPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
{
|
{
|
||||||
new EditBuildScreenZoomPatch().Enable();
|
return AccessTools.Method(typeof(EditBuildScreen), nameof(EditBuildScreen.Awake));
|
||||||
new WeaponModdingScreenZoomPatch().Enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EditBuildScreenZoomPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix(EditBuildScreen __instance, WeaponPreview ____weaponPreview)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var scrollTrigger = __instance.gameObject.AddComponent<ScrollTrigger>();
|
||||||
|
scrollTrigger.OnOnScroll += (PointerEventData eventData) =>
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(EditBuildScreen), nameof(EditBuildScreen.Awake));
|
if (____weaponPreview != null && __instance != null)
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(EditBuildScreen __instance, WeaponPreview ____weaponPreview)
|
|
||||||
{
|
|
||||||
var scrollTrigger = __instance.gameObject.AddComponent<ScrollTrigger>();
|
|
||||||
scrollTrigger.OnOnScroll += (PointerEventData eventData) =>
|
|
||||||
{
|
{
|
||||||
if (____weaponPreview != null && __instance != null)
|
____weaponPreview.Zoom(eventData.scrollDelta.y * 0.12f);
|
||||||
{
|
__instance.UpdatePositions();
|
||||||
____weaponPreview.Zoom(eventData.scrollDelta.y * 0.12f);
|
}
|
||||||
__instance.UpdatePositions();
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
|
||||||
|
public class WeaponModdingScreenZoomPatch : ModulePatch
|
||||||
|
{
|
||||||
|
protected override MethodBase GetTargetMethod()
|
||||||
|
{
|
||||||
|
return AccessTools.Method(typeof(WeaponModdingScreen), nameof(WeaponModdingScreen.Awake));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WeaponModdingScreenZoomPatch : ModulePatch
|
[PatchPrefix]
|
||||||
|
public static void Prefix(WeaponModdingScreen __instance, WeaponPreview ____weaponPreview)
|
||||||
{
|
{
|
||||||
protected override MethodBase GetTargetMethod()
|
var scrollTrigger = __instance.gameObject.AddComponent<ScrollTrigger>();
|
||||||
|
scrollTrigger.OnOnScroll += (PointerEventData eventData) =>
|
||||||
{
|
{
|
||||||
return AccessTools.Method(typeof(WeaponModdingScreen), nameof(WeaponModdingScreen.Awake));
|
if (____weaponPreview != null && __instance != null)
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void Prefix(WeaponModdingScreen __instance, WeaponPreview ____weaponPreview)
|
|
||||||
{
|
|
||||||
var scrollTrigger = __instance.gameObject.AddComponent<ScrollTrigger>();
|
|
||||||
scrollTrigger.OnOnScroll += (PointerEventData eventData) =>
|
|
||||||
{
|
{
|
||||||
if (____weaponPreview != null && __instance != null)
|
____weaponPreview.Zoom(eventData.scrollDelta.y * 0.12f);
|
||||||
{
|
__instance.UpdatePositions();
|
||||||
____weaponPreview.Zoom(eventData.scrollDelta.y * 0.12f);
|
}
|
||||||
__instance.UpdatePositions();
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
125
Plugin.cs
125
Plugin.cs
@@ -2,73 +2,72 @@
|
|||||||
using Comfort.Common;
|
using Comfort.Common;
|
||||||
using EFT;
|
using EFT;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
|
||||||
|
public class Plugin : BaseUnityPlugin
|
||||||
{
|
{
|
||||||
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
|
public void Awake()
|
||||||
public class Plugin : BaseUnityPlugin
|
|
||||||
{
|
{
|
||||||
public void Awake()
|
Settings.Init(Config);
|
||||||
{
|
|
||||||
Settings.Init(Config);
|
|
||||||
|
|
||||||
R.Init();
|
R.Init();
|
||||||
|
|
||||||
ConfirmDialogKeysPatches.Enable();
|
ConfirmDialogKeysPatches.Enable();
|
||||||
new FixMailRecieveAllPatch().Enable();
|
new FixMailRecieveAllPatch().Enable();
|
||||||
FixTooltipPatches.Enable();
|
FixTooltipPatches.Enable();
|
||||||
QuickAccessPanelPatches.Enable();
|
QuickAccessPanelPatches.Enable();
|
||||||
FocusFleaOfferNumberPatches.Enable();
|
FocusFleaOfferNumberPatches.Enable();
|
||||||
HideoutSearchPatches.Enable();
|
HideoutSearchPatches.Enable();
|
||||||
HideoutLevelPatches.Enable();
|
HideoutLevelPatches.Enable();
|
||||||
InspectWindowResizePatches.Enable();
|
InspectWindowResizePatches.Enable();
|
||||||
InspectWindowStatsPatches.Enable();
|
InspectWindowStatsPatches.Enable();
|
||||||
new RemoveDoorActionsPatch().Enable();
|
new RemoveDoorActionsPatch().Enable();
|
||||||
ScrollPatches.Enable();
|
ScrollPatches.Enable();
|
||||||
StackFirItemsPatches.Enable();
|
StackFirItemsPatches.Enable();
|
||||||
SwapPatches.Enable();
|
SwapPatches.Enable();
|
||||||
SyncScrollPositionPatches.Enable();
|
SyncScrollPositionPatches.Enable();
|
||||||
new TransferConfirmPatch().Enable();
|
new TransferConfirmPatch().Enable();
|
||||||
WeaponPresetConfirmPatches.Enable();
|
WeaponPresetConfirmPatches.Enable();
|
||||||
WeaponZoomPatches.Enable();
|
WeaponZoomPatches.Enable();
|
||||||
new MoveTaskbarPatch().Enable();
|
new MoveTaskbarPatch().Enable();
|
||||||
FixFleaPatches.Enable();
|
FixFleaPatches.Enable();
|
||||||
FleaPrevSearchPatches.Enable();
|
FleaPrevSearchPatches.Enable();
|
||||||
KeepOfferWindowOpenPatches.Enable();
|
KeepOfferWindowOpenPatches.Enable();
|
||||||
AddOfferClickablePricesPatches.Enable();
|
AddOfferClickablePricesPatches.Enable();
|
||||||
new AssortUnlocksPatch().Enable();
|
new AssortUnlocksPatch().Enable();
|
||||||
new AutofillQuestItemsPatch().Enable();
|
new AutofillQuestItemsPatch().Enable();
|
||||||
ContextMenuPatches.Enable();
|
ContextMenuPatches.Enable();
|
||||||
TradingAutoSwitchPatches.Enable();
|
TradingAutoSwitchPatches.Enable();
|
||||||
AddOfferRememberAutoselectPatches.Enable();
|
AddOfferRememberAutoselectPatches.Enable();
|
||||||
KeepMessagesOpenPatches.Enable();
|
KeepMessagesOpenPatches.Enable();
|
||||||
new FocusTradeQuantityPatch().Enable();
|
new FocusTradeQuantityPatch().Enable();
|
||||||
RememberRepairerPatches.Enable();
|
RememberRepairerPatches.Enable();
|
||||||
new GridWindowButtonsPatch().Enable();
|
new GridWindowButtonsPatch().Enable();
|
||||||
new LoadMagPresetsPatch().Enable();
|
new LoadMagPresetsPatch().Enable();
|
||||||
KeepWindowsOnScreenPatches.Enable();
|
KeepWindowsOnScreenPatches.Enable();
|
||||||
ContextMenuShortcutPatches.Enable();
|
ContextMenuShortcutPatches.Enable();
|
||||||
new OpenSortingTablePatch().Enable();
|
new OpenSortingTablePatch().Enable();
|
||||||
LoadAmmoInRaidPatches.Enable();
|
LoadAmmoInRaidPatches.Enable();
|
||||||
MultiSelectPatches.Enable();
|
MultiSelectPatches.Enable();
|
||||||
new FixUnloadLastBulletPatch().Enable();
|
new FixUnloadLastBulletPatch().Enable();
|
||||||
StackMoveGreedyPatches.Enable();
|
StackMoveGreedyPatches.Enable();
|
||||||
UnloadAmmoPatches.Enable();
|
UnloadAmmoPatches.Enable();
|
||||||
new FixTraderControllerSimulateFalsePatch().Enable();
|
new FixTraderControllerSimulateFalsePatch().Enable();
|
||||||
LoadMultipleMagazinesPatches.Enable();
|
LoadMultipleMagazinesPatches.Enable();
|
||||||
new PutToolsBackPatch().Enable();
|
new PutToolsBackPatch().Enable();
|
||||||
new RebindGrenadesPatch().Enable();
|
new RebindGrenadesPatch().Enable();
|
||||||
AimToggleHoldPatches.Enable();
|
AimToggleHoldPatches.Enable();
|
||||||
ReorderGridsPatches.Enable();
|
ReorderGridsPatches.Enable();
|
||||||
NoRandomGrenadesPatch.Init();
|
NoRandomGrenadesPatch.Init();
|
||||||
GPCoinPatches.Enable();
|
GPCoinPatches.Enable();
|
||||||
FleaSlotSearchPatches.Enable();
|
FleaSlotSearchPatches.Enable();
|
||||||
MoveSortingTablePatches.Enable();
|
MoveSortingTablePatches.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool InRaid()
|
public static bool InRaid()
|
||||||
{
|
{
|
||||||
bool? inRaid = Singleton<AbstractGame>.Instance?.InRaid;
|
bool? inRaid = Singleton<AbstractGame>.Instance?.InRaid;
|
||||||
return inRaid.HasValue && inRaid.Value;
|
return inRaid.HasValue && inRaid.Value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1468
Settings.cs
1468
Settings.cs
File diff suppressed because it is too large
Load Diff
@@ -3,77 +3,76 @@ using System.Collections.Generic;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public class TaskSerializer<T> : MonoBehaviour
|
||||||
{
|
{
|
||||||
public class TaskSerializer<T> : MonoBehaviour
|
private Func<T, Task> func;
|
||||||
|
private Func<T, bool> canContinue;
|
||||||
|
private IEnumerator<T> enumerator;
|
||||||
|
private Task currentTask;
|
||||||
|
private TaskCompletionSource totalTask;
|
||||||
|
|
||||||
|
public Task Initialize(IEnumerable<T> items, Func<T, Task> func, Func<T, bool> canContinue = null)
|
||||||
{
|
{
|
||||||
private Func<T, Task> func;
|
this.enumerator = items.GetEnumerator();
|
||||||
private Func<T, bool> canContinue;
|
this.func = func;
|
||||||
private IEnumerator<T> enumerator;
|
this.canContinue = canContinue;
|
||||||
private Task currentTask;
|
|
||||||
private TaskCompletionSource totalTask;
|
|
||||||
|
|
||||||
public Task Initialize(IEnumerable<T> items, Func<T, Task> func, Func<T, bool> canContinue = null)
|
currentTask = Task.CompletedTask;
|
||||||
|
totalTask = new TaskCompletionSource();
|
||||||
|
|
||||||
|
LateUpdate();
|
||||||
|
|
||||||
|
return totalTask.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Cancel()
|
||||||
|
{
|
||||||
|
if (!totalTask.Task.IsCompleted)
|
||||||
{
|
{
|
||||||
this.enumerator = items.GetEnumerator();
|
totalTask.TrySetCanceled();
|
||||||
this.func = func;
|
Complete();
|
||||||
this.canContinue = canContinue;
|
|
||||||
|
|
||||||
currentTask = Task.CompletedTask;
|
|
||||||
totalTask = new TaskCompletionSource();
|
|
||||||
|
|
||||||
LateUpdate();
|
|
||||||
|
|
||||||
return totalTask.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Cancel()
|
|
||||||
{
|
|
||||||
if (!totalTask.Task.IsCompleted)
|
|
||||||
{
|
|
||||||
totalTask.TrySetCanceled();
|
|
||||||
Complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnDisable()
|
|
||||||
{
|
|
||||||
Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LateUpdate()
|
|
||||||
{
|
|
||||||
if (currentTask.IsCanceled)
|
|
||||||
{
|
|
||||||
Complete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalTask.Task.IsCompleted || !currentTask.IsCompleted)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canContinue != null && enumerator.Current != null && !canContinue(enumerator.Current))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enumerator.MoveNext())
|
|
||||||
{
|
|
||||||
currentTask = func(enumerator.Current);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Complete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Complete()
|
|
||||||
{
|
|
||||||
totalTask.TryComplete();
|
|
||||||
func = null;
|
|
||||||
Destroy(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnDisable()
|
||||||
|
{
|
||||||
|
Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LateUpdate()
|
||||||
|
{
|
||||||
|
if (currentTask.IsCanceled)
|
||||||
|
{
|
||||||
|
Complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalTask.Task.IsCompleted || !currentTask.IsCompleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canContinue != null && enumerator.Current != null && !canContinue(enumerator.Current))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
currentTask = func(enumerator.Current);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Complete()
|
||||||
|
{
|
||||||
|
totalTask.TryComplete();
|
||||||
|
func = null;
|
||||||
|
Destroy(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
145
ToggleHold.cs
145
ToggleHold.cs
@@ -1,86 +1,85 @@
|
|||||||
using EFT.InputSystem;
|
using EFT.InputSystem;
|
||||||
|
|
||||||
namespace UIFixes
|
namespace UIFixes;
|
||||||
|
|
||||||
|
public enum ToggleHoldState
|
||||||
{
|
{
|
||||||
public enum ToggleHoldState
|
Idle = 13,
|
||||||
|
ClickOrHold = 14,
|
||||||
|
Holding = 15
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ToggleHoldIdleState(KeyCombination keyCombination) : KeyCombination.KeyCombinationState(keyCombination)
|
||||||
|
{
|
||||||
|
public override ECommand GetCommand(float deltaTime)
|
||||||
{
|
{
|
||||||
Idle = 13,
|
if (!CanProcess())
|
||||||
ClickOrHold = 14,
|
|
||||||
Holding = 15
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ToggleHoldIdleState(KeyCombination keyCombination) : KeyCombination.KeyCombinationState(keyCombination)
|
|
||||||
{
|
|
||||||
public override ECommand GetCommand(float deltaTime)
|
|
||||||
{
|
{
|
||||||
if (!CanProcess())
|
|
||||||
{
|
|
||||||
return ECommand.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
HandleKeys(false);
|
|
||||||
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.ClickOrHold);
|
|
||||||
return GetCommandInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool CanProcess()
|
|
||||||
{
|
|
||||||
return GetKeysStatus(out EKeyPress ekeyPress) && (ekeyPress == EKeyPress.Down);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ToggleHoldClickOrHoldState(KeyCombination keyCombination) : KeyCombination.KeyCombinationState(keyCombination)
|
|
||||||
{
|
|
||||||
public override void Enter()
|
|
||||||
{
|
|
||||||
timer = KeyCombination.DoubleClickTimeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ECommand GetCommand(float deltaTime)
|
|
||||||
{
|
|
||||||
if (GetKeysStatus(out EKeyPress ekeyPress))
|
|
||||||
{
|
|
||||||
if (ekeyPress == EKeyPress.Hold)
|
|
||||||
{
|
|
||||||
HandleKeys(false);
|
|
||||||
if (LongEnough(deltaTime))
|
|
||||||
{
|
|
||||||
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.Holding);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ECommand.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UnhandleKeys(null);
|
|
||||||
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle);
|
|
||||||
return ECommand.None;
|
return ECommand.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool LongEnough(float deltaTime)
|
HandleKeys(false);
|
||||||
{
|
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.ClickOrHold);
|
||||||
timer -= deltaTime;
|
return GetCommandInternal();
|
||||||
return timer <= 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float timer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ToggleHoldHoldState(KeyCombination keyCombination, ECommand disableCommand) : KeyCombination.KeyCombinationState(keyCombination)
|
protected bool CanProcess()
|
||||||
{
|
{
|
||||||
private readonly ECommand disableCommand = disableCommand;
|
return GetKeysStatus(out EKeyPress ekeyPress) && (ekeyPress == EKeyPress.Down);
|
||||||
|
}
|
||||||
public override ECommand GetCommand(float deltaTime)
|
}
|
||||||
{
|
|
||||||
if (GetKeysStatus(out EKeyPress ekeyPress) && ekeyPress == EKeyPress.Hold)
|
public class ToggleHoldClickOrHoldState(KeyCombination keyCombination) : KeyCombination.KeyCombinationState(keyCombination)
|
||||||
{
|
{
|
||||||
HandleKeys(false);
|
public override void Enter()
|
||||||
return ECommand.None;
|
{
|
||||||
}
|
timer = KeyCombination.DoubleClickTimeout;
|
||||||
|
}
|
||||||
UnhandleKeys(null);
|
|
||||||
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle);
|
public override ECommand GetCommand(float deltaTime)
|
||||||
return disableCommand;
|
{
|
||||||
}
|
if (GetKeysStatus(out EKeyPress ekeyPress))
|
||||||
|
{
|
||||||
|
if (ekeyPress == EKeyPress.Hold)
|
||||||
|
{
|
||||||
|
HandleKeys(false);
|
||||||
|
if (LongEnough(deltaTime))
|
||||||
|
{
|
||||||
|
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.Holding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ECommand.None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnhandleKeys(null);
|
||||||
|
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle);
|
||||||
|
return ECommand.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LongEnough(float deltaTime)
|
||||||
|
{
|
||||||
|
timer -= deltaTime;
|
||||||
|
return timer <= 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ToggleHoldHoldState(KeyCombination keyCombination, ECommand disableCommand) : KeyCombination.KeyCombinationState(keyCombination)
|
||||||
|
{
|
||||||
|
private readonly ECommand disableCommand = disableCommand;
|
||||||
|
|
||||||
|
public override ECommand GetCommand(float deltaTime)
|
||||||
|
{
|
||||||
|
if (GetKeysStatus(out EKeyPress ekeyPress) && ekeyPress == EKeyPress.Hold)
|
||||||
|
{
|
||||||
|
HandleKeys(false);
|
||||||
|
return ECommand.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnhandleKeys(null);
|
||||||
|
KeyCombination.method_0((KeyCombination.EKeyState)ToggleHoldState.Idle);
|
||||||
|
return disableCommand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user