├── EnhancedInventory ├── Repository.json ├── src │ ├── Util │ │ ├── Extensions.cs │ │ ├── RemappableInt.cs │ │ ├── Enum.cs │ │ └── SearchBar.cs │ ├── Hooks │ │ ├── VendorVM.cs │ │ ├── LootVM.cs │ │ ├── ItemsFilterPCView.cs │ │ └── ItemsFilter.cs │ ├── ModInterop.cs │ ├── Localization │ │ └── Strings.cs │ ├── Settings │ │ ├── Settings.cs │ │ └── SettingsGUI.cs │ ├── Events │ │ └── OnAreaLoad.cs │ ├── Main.cs │ └── Controllers │ │ ├── LootHighlightController.cs │ │ ├── InventoryController.cs │ │ └── SpellbookController.cs ├── Info.json ├── Properties │ ├── Resources.zh-CN.resx │ ├── Resources.resx │ └── Resources.Designer.cs └── EnhancedInventory.csproj ├── LICENSE ├── README.md ├── EnhancedInventory.sln └── .gitignore /EnhancedInventory/Repository.json: -------------------------------------------------------------------------------- 1 | { 2 | "Releases": 3 | [ 4 | {"Id": "EnhancedInventory", "Version": "1.0.10"} 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Util/Extensions.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace EnhancedInventory.Util 4 | { 5 | public static class Extensions 6 | { 7 | public static string GetPath(this Transform current) 8 | { 9 | if (current.parent == null) 10 | { 11 | return "/" + current.name; 12 | } 13 | 14 | return current.parent.GetPath() + "/" + current.name; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /EnhancedInventory/Info.json: -------------------------------------------------------------------------------- 1 | { 2 | "Id": "EnhancedInventory", 3 | "DisplayName": "Enhanced Inventory", 4 | "Author": "Xenofell", 5 | "Version": "1.0.10", 6 | "ManagerVersion": "0.23.3", 7 | "LoadAfter": [ "InventorySearchBar", "WeightValueSorting", "HighlightImportantLoot" ], // legacy mods 8 | "AssemblyName": "EnhancedInventory.dll", 9 | "EntryMethod": "EnhancedInventory.Main.Load", 10 | "Repository": "https://raw.githubusercontent.com/cstamford/WOTR_EnhancedInventory/main/EnhancedInventory/Repository.json", 11 | "HomePage": "https://www.nexusmods.com/pathfinderwrathoftherighteous/mods/137" 12 | } 13 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Util/RemappableInt.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace EnhancedInventory.Util 4 | { 5 | public class RemappableInt 6 | { 7 | private readonly List m_mapping = new List(); 8 | 9 | public void Add(int to) 10 | { 11 | m_mapping.Add(to); 12 | } 13 | 14 | public void Clear() 15 | { 16 | m_mapping.Clear(); 17 | } 18 | 19 | public int To(int idx) 20 | { 21 | return m_mapping[idx]; 22 | } 23 | 24 | public int From(int idx) 25 | { 26 | return m_mapping.IndexOf(idx); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Hooks/VendorVM.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker.UI.MVVM._VM.Vendor; 3 | 4 | namespace EnhancedInventory.Hooks 5 | { 6 | // For some reason the vendor slots group changed notification is not dispatched, it is handled immediately, so we dispatch it here. 7 | // This is safe to do whether we're enabled or not - if we're disabled, the notification will simply fall upon deaf ears. 8 | [HarmonyPatch(typeof(VendorVM), nameof(VendorVM.UpdateVendorSide))] 9 | public static class VendorVM_UpdateVendorSide 10 | { 11 | [HarmonyPostfix] 12 | public static void Postfix(VendorVM __instance) 13 | { 14 | __instance.VendorSlotsGroup.CollectionChangedCommand.Execute(false); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EnhancedInventory/src/ModInterop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace EnhancedInventory 6 | { 7 | public static class ModInterop 8 | { 9 | private static readonly string[] m_legacy_mods = new string[] 10 | { 11 | "InventorySearchBar", 12 | "WeightValueSorting", 13 | "HighlightImportantLoot" 14 | }; 15 | 16 | public static bool HasModConflicts() 17 | { 18 | IEnumerable legacy_conflicts = AppDomain.CurrentDomain.GetAssemblies() 19 | .Where(i => m_legacy_mods.Any(j => i.FullName.Contains(j))).Select(i => i.FullName); 20 | 21 | if (legacy_conflicts.Any()) 22 | { 23 | Main.Logger.Error("You have one or more legacy mods running. " + 24 | "Enhanced Inventory contains their functionality, please disable them!:\n" + 25 | legacy_conflicts.Aggregate((i, j) => i + "\n" + j)); 26 | 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Enhanced Inventory 2 | 3 | Enhances the inventory experience. Adds features such as: a search bar to all inventory screens, additional filtering and sorting options, customizable filter and sort lists, highlight important loot in the loot window. Options are highly configurable through the UMM mod options screen. 4 | 5 | # How to install 6 | 7 | 0. Download the latest published zip file. 8 | 1. Install [UnityModManager](https://www.nexusmods.com/site/mods/21) 9 | 2. Install the zip with UnityModManager. 10 | 11 | # How to Compile 12 | 13 | 0. Install all required development pre-requisites: 14 | - [Visual Studio 2019 Community Edition](https://visualstudio.microsoft.com/downloads/) 15 | - [.NET "Current" x86 SDK](https://dotnet.microsoft.com/download/visual-studio-sdks) 16 | 1. Download and install [Unity Mod Manager (UMM)](https://www.nexusmods.com/site/mods/21) 17 | 2. Execute UMM, Select Pathfinder: WoTR, and Install 18 | 3. Create the environment variable *WrathInstallDir* and point it to your Pathfinder: WoTR game home folder 19 | - tip: search for "edit the system environment variables" on windows search bar45. Use "Install Release" or "Install Debug" to have the Mod installed directly to your Game Mods folder 20 | 4. Run [AssemblyPublicizer](https://github.com/CabbageCrow/AssemblyPublicizer) on the WotR Assembly-CSharp.dll inside the WotR folder you set earlier. 21 | 5. Build! If you get assembly reference errors, check the project file and make sure your publicized assembly is in the correct location. 22 | 23 | # Links 24 | 25 | Source code: https://github.com/cstamford/WOTR_EnhancedInventory 26 | Nexus: https://www.nexusmods.com/pathfinderwrathoftherighteous/mods/137 27 | -------------------------------------------------------------------------------- /EnhancedInventory.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30611.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EnhancedInventory", "EnhancedInventory\EnhancedInventory.csproj", "{2CAEA425-CE96-4E2A-8248-FD55675B20F6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug Install|Any CPU = Debug Install|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | Release Install|Any CPU = Release Install|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Debug Install|Any CPU.ActiveCfg = Debug Install|Any CPU 19 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Debug Install|Any CPU.Build.0 = Debug Install|Any CPU 20 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Release Install|Any CPU.ActiveCfg = Release Install|Any CPU 23 | {2CAEA425-CE96-4E2A-8248-FD55675B20F6}.Release Install|Any CPU.Build.0 = Release Install|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {72A74A8E-D4AB-4C28-B389-AD1BA031A590} 30 | EndGlobalSection 31 | EndGlobal 32 | 33 | 34 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Localization/Strings.cs: -------------------------------------------------------------------------------- 1 | using RES = EnhancedInventory.Properties.Resources; 2 | 3 | namespace EnhancedInventory.Localization 4 | { 5 | public static class InventoryStrings 6 | { 7 | public static string EnterItemName = RES.InventorySearchBarText; 8 | public static string QuickslotUtilities = RES.InventoryQuickslotUtilitiesText; 9 | public static string UnlearnedScrolls = RES.InventoryUnlearnedScrollsText; 10 | public static string UnlearnedRecipes = RES.InventoryUnlearnedRecipesText; 11 | public static string UnreadDocuments = RES.InventoryUnreadDocumentsText; 12 | public static string UsableWithoutUMDCheck = RES.InventoryUsableWithoutUMDText; 13 | public static string CurrentEquipped = RES.InventoryCurrentEquippedText; 14 | public static string NonZeroPW = RES.InventoryNonZeroPWText; 15 | public static string PriceWeightAscending = RES.InventoryPriceWeightAscendingText; 16 | public static string PriceWeightDescending = RES.InventoryPriceWeightDescendingText; 17 | } 18 | 19 | public static class SpellbookStrings 20 | { 21 | public static string EnterSpellName = RES.SpellbookSearchBarText; 22 | public static string NoFilter = RES.SpellbookSearchFilterNoFilterText; 23 | public static string FilterTargets = RES.SpellbookSearchFilterTargetsText; 24 | public static string ShowAllSpellLevels = RES.SpellbookShowAllSpellLevelsCheckboxText; 25 | public static string ShowMetamagic = RES.SpellbookShowMetamagicCheckboxText; 26 | public static string ShowUnlearnedSpells = RES.SpellbookShowUnlearnedSpellsCheckboxText; 27 | public static string LearnScrolls = RES.SpellbookLearnableScrollsButtonText; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Util/Enum.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Settings; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace EnhancedInventory.Util 7 | { 8 | public enum ExpandedFilterType 9 | { 10 | QuickslotUtilities = 8, 11 | UnlearnedScrolls = 9, 12 | UnlearnedRecipes = 10, 13 | UnreadDocuments = 11, 14 | UsableWithoutUMD = 12, 15 | CurrentEquipped = 13, 16 | NonZeroPW = 14, 17 | } 18 | 19 | public enum ExpandedSorterType 20 | { 21 | WeightValueUp = 11, 22 | WeightValueDown = 12 23 | } 24 | 25 | public enum SpellbookFilter 26 | { 27 | NoFilter, 28 | TargetsFortitude, 29 | TargetsReflex, 30 | TargetsWill 31 | } 32 | 33 | public static class EnumHelper 34 | { 35 | public static IEnumerable ValidInventorySearchCriteria 36 | = Enum.GetValues(typeof(InventorySearchCriteria)).Cast().Where(i => i != InventorySearchCriteria.Default); 37 | 38 | public static IEnumerable ValidSpellbookSearchCriteria 39 | = Enum.GetValues(typeof(SpellbookSearchCriteria)).Cast().Where(i => i != SpellbookSearchCriteria.Default); 40 | 41 | public static IEnumerable ValidHighlightLootableOptions 42 | = Enum.GetValues(typeof(HighlightLootableOptions)).Cast().Where(i => i != HighlightLootableOptions.Default); 43 | 44 | public static IEnumerable ValidFilterCategories 45 | = Enum.GetValues(typeof(FilterCategories)).Cast().Where(i => i != FilterCategories.Default); 46 | 47 | public static IEnumerable ValidSorterCategories 48 | = Enum.GetValues(typeof(SorterCategories)).Cast().Where(i => i != SorterCategories.Default); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Hooks/LootVM.cs: -------------------------------------------------------------------------------- 1 | using HarmonyLib; 2 | using Kingmaker; 3 | using Kingmaker.Blueprints; 4 | using Kingmaker.Blueprints.Items.Components; 5 | using Kingmaker.Items; 6 | using Kingmaker.Items.Parts; 7 | using Kingmaker.UI.MVVM._VM.Loot; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Reflection.Emit; 12 | 13 | namespace EnhancedInventory.Hooks 14 | { 15 | // This patch redirects the foreach loop to our function which filters each item based on settings. 16 | [HarmonyPatch(typeof(LootObjectVM), nameof(LootObjectVM.TransferAllItems))] 17 | public static class LootObjectVM_TransferAllItems 18 | { 19 | private static MethodInfo Method__FilterItem = AccessTools.Method(typeof(LootObjectVM_TransferAllItems), nameof(FilterItem)); 20 | 21 | private static bool FilterItem(ItemEntity item) 22 | { 23 | if (!Main.Settings.EnableCollectAllTweaks) return true; 24 | 25 | if (Main.Settings.CollectAllZeroWeightItems && item.Blueprint.Weight <= 0.0f) return true; 26 | if (Main.Settings.CollectAllUnidentifiedItems && !item.IsIdentified) return true; 27 | if (Main.Settings.CollectAllWeightValue && item.Blueprint.Cost / item.Blueprint.Weight >= Main.Settings.CollectAllWeightValueCutoff) return true; 28 | if (Main.Settings.CollectAllNotableItems && item.Blueprint.IsNotable) return true; 29 | 30 | CopyScroll scroll = item.Blueprint.GetComponent(); 31 | CopyRecipe recipe = item.Blueprint.GetComponent(); 32 | ItemPartShowInfoCallback cb = item.Get(); 33 | bool is_copyable_scroll = scroll != null && Game.Instance.Player.Party.Any(i => scroll.CanCopy(item, i)); 34 | bool is_unlearned_recipe = recipe != null && Game.Instance.Player.Party.Any(i => recipe.CanCopy(item, i)); 35 | bool is_unread_document = cb != null && (!cb.m_Settings.Once || !cb.m_Triggered); 36 | 37 | return Main.Settings.CollectAllUsefulItems && (is_copyable_scroll || is_unlearned_recipe || is_unread_document); 38 | } 39 | 40 | [HarmonyTranspiler] 41 | public static IEnumerable Transpiler(IEnumerable instructions) 42 | { 43 | List il = instructions.ToList(); 44 | 45 | for (int i = 0; i < il.Count; ++i) 46 | { 47 | if (il[i].opcode == OpCodes.Stloc_1) 48 | { 49 | il.Insert(++i, new CodeInstruction(OpCodes.Ldloc_1)); // load ItemEntity 50 | il.Insert(++i, new CodeInstruction(OpCodes.Call, Method__FilterItem)); // call FilterItem 51 | 52 | // Scan back for the br.s, then with its label, insert a jump to next element if we're false. 53 | for (int j = i - 2; j >= 0; --j) 54 | { 55 | if (il[j].opcode == OpCodes.Br_S) 56 | { 57 | il.Insert(++i, new CodeInstruction(OpCodes.Brfalse_S, il[j].operand)); // jump to next element 58 | break; 59 | } 60 | } 61 | 62 | break; 63 | } 64 | } 65 | 66 | return il.AsEnumerable(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Hooks/ItemsFilterPCView.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Settings; 2 | using EnhancedInventory.Util; 3 | using HarmonyLib; 4 | using Kingmaker.Blueprints.Root; 5 | using Kingmaker.UI.Common; 6 | using Kingmaker.UI.MVVM._PCView.Slots; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Reflection; 11 | using System.Reflection.Emit; 12 | using UniRx; 13 | 14 | namespace EnhancedInventory.Hooks 15 | { 16 | // Handles both adding selected sorters to the sorter dropdowns and making sure that the dropdown is properly updates to match the selected sorter. 17 | [HarmonyPatch(typeof(ItemsFilterPCView))] 18 | public static class ItemsFilterPCView_ 19 | { 20 | private static readonly MethodInfo[] m_cbs = new MethodInfo[] 21 | { 22 | AccessTools.Method(typeof(ItemsFilterPCView_), nameof(SetDropdown)), 23 | AccessTools.Method(typeof(ItemsFilterPCView_), nameof(SetSorter)), 24 | AccessTools.Method(typeof(ItemsFilterPCView_), nameof(ObserveFilterChange)), 25 | }; 26 | 27 | private static void SetDropdown(ItemsFilterPCView instance, ItemsFilter.SorterType val) 28 | { 29 | instance.m_Sorter.value = Main.SorterMapper.From((int)val); 30 | } 31 | 32 | private static void SetSorter(ItemsFilterPCView instance, int val) 33 | { 34 | instance.ViewModel.SetCurrentSorter((ItemsFilter.SorterType)Main.SorterMapper.To(val)); 35 | } 36 | 37 | private static ItemsFilter.FilterType _last_filter; 38 | 39 | private static void ObserveFilterChange(ItemsFilterPCView instance, ItemsFilter.FilterType filter) 40 | { 41 | if (_last_filter != filter) 42 | { 43 | _last_filter = filter; 44 | instance.ScrollToTop(); 45 | } 46 | } 47 | 48 | // In BindViewImplementation, there are two inline delegates; we replace both of those in order with our own. 49 | [HarmonyTranspiler] 50 | [HarmonyPatch(nameof(ItemsFilterPCView.BindViewImplementation))] 51 | public static IEnumerable BindViewImplementation(IEnumerable instructions) 52 | { 53 | List il = instructions.ToList(); 54 | 55 | int ldftn_count = 0; 56 | 57 | for (int i = 0; i < il.Count && ldftn_count < m_cbs.Length; ++i) 58 | { 59 | if (il[i].opcode == OpCodes.Ldftn) 60 | { 61 | il[i].operand = m_cbs[ldftn_count++]; 62 | } 63 | } 64 | 65 | return il.AsEnumerable(); 66 | } 67 | 68 | // Adds the sorters to the dropdown. 69 | [HarmonyPostfix] 70 | [HarmonyPatch(nameof(ItemsFilterPCView.Initialize), new Type[] { })] 71 | public static void Initialize(ItemsFilterPCView __instance) 72 | { 73 | __instance.m_Sorter.ClearOptions(); 74 | 75 | List options = new List(); 76 | 77 | foreach (SorterCategories flag in EnumHelper.ValidSorterCategories) 78 | { 79 | if (Main.Settings.SorterOptions.HasFlag(flag)) 80 | { 81 | (int idx, string text) = Main.SorterCategoryMap[flag]; 82 | 83 | if (text == null) 84 | { 85 | text = LocalizedTexts.Instance.ItemsFilter.GetText((ItemsFilter.SorterType)idx); 86 | Main.SorterCategoryMap[flag] = (idx, text); 87 | } 88 | 89 | options.Add(text); 90 | } 91 | } 92 | 93 | __instance.m_Sorter.AddOptions(options); 94 | } 95 | 96 | [HarmonyPrefix] 97 | [HarmonyPatch(nameof(ItemsFilterPCView.Initialize), new Type[] { typeof(bool) })] 98 | public static void Initialize_Prefix(ref bool needReset) 99 | { 100 | needReset = false; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Util/SearchBar.cs: -------------------------------------------------------------------------------- 1 | using Kingmaker; 2 | using Kingmaker.UI.MVVM._PCView.CharGen.Phases.FeatureSelector; 3 | using Owlcat.Runtime.UI.Controls.Button; 4 | using TMPro; 5 | using UnityEngine; 6 | using UnityEngine.EventSystems; 7 | 8 | namespace EnhancedInventory.Util 9 | { 10 | public class SearchBar 11 | { 12 | public GameObject GameObject; 13 | public TMP_InputField InputField; 14 | public OwlcatButton InputButton; 15 | public TMP_Dropdown Dropdown; 16 | public OwlcatButton DropdownButton; 17 | public GameObject DropdownIconObject; 18 | public TextMeshProUGUI PlaceholderText; 19 | 20 | public SearchBar(Transform parent, string placeholder, string name = "EnhancedInventory_SearchBar") 21 | { 22 | Transform prefab_transform = Game.Instance.UI.MainCanvas.transform.Find("ChargenPCView/ContentWrapper/DetailedViewZone/ChargenFeaturesDetailedPCView/FeatureSelectorPlace/FeatureSelectorView/FeatureSearchView"); 23 | 24 | if (prefab_transform == null) 25 | { 26 | string err = "Error: Unable to locate search bar prefab, it's likely a patch has changed the UI setup, or you are in an unexpected situation. Please report this bug!"; 27 | Main.Logger.Error(err); 28 | throw new UnityException(err); 29 | } 30 | 31 | GameObject = GameObject.Instantiate(prefab_transform, parent, false).gameObject; 32 | GameObject.name = name; 33 | 34 | InputButton = GameObject.transform.Find("FieldPlace/SearchField/SearchBackImage/Placeholder").GetComponent(); 35 | Dropdown = GameObject.transform.Find("FieldPlace/SearchField/SearchBackImage/Dropdown").GetComponent(); 36 | DropdownButton = GameObject.transform.Find("FieldPlace/SearchField/SearchBackImage/Dropdown/GenerateButtonPlace").GetComponent(); 37 | DropdownIconObject = GameObject.transform.Find("FieldPlace/SearchField/SearchBackImage/Dropdown/GenerateButtonPlace/GenerateButton/Icon").gameObject; 38 | PlaceholderText = GameObject.transform.Find("FieldPlace/SearchField/SearchBackImage/Placeholder/Label").GetComponent(); 39 | InputField = GameObject.transform.Find("FieldPlace/SearchField/SearchBackImage/InputField").GetComponent(); 40 | 41 | InputField.onValueChanged.AddListener(delegate (string _) { OnInputFieldEdit(); }); 42 | InputField.onEndEdit.AddListener(delegate (string _) { OnInputFieldEditEnd(); }); 43 | InputButton.OnLeftClick.AddListener(delegate { OnInputClick(); }); 44 | Dropdown.onValueChanged.AddListener(delegate (int _) { OnDropdownSelected(); }); 45 | DropdownButton.OnLeftClick.AddListener(delegate { OnDropdownButton(); }); 46 | 47 | GameObject.Destroy(GameObject.GetComponent()); // controller from where we stole the search bar 48 | InputField.transform.Find("Text Area/Placeholder").GetComponent().SetText(placeholder); 49 | Dropdown.ClearOptions(); 50 | 51 | GameObject.Destroy(Dropdown.template.Find("Viewport/TopBorderImage").gameObject); 52 | Transform border = Dropdown.template.Find("Viewport/Content/Item/BottomBorderImage"); 53 | RectTransform rect = border.GetComponent(); 54 | rect.anchorMin = new Vector2(0.0f, 0.0f); 55 | rect.anchorMax = new Vector2(1.0f, 0.0f); 56 | rect.offsetMin = new Vector2(0.0f, 0.0f); 57 | rect.offsetMax = new Vector2(0.0f, 2.0f); 58 | } 59 | 60 | public void FocusSearchBar() 61 | { 62 | OnInputClick(); 63 | } 64 | 65 | public void UpdatePlaceholder() 66 | { 67 | PlaceholderText.text = string.IsNullOrEmpty(InputField.text) ? Dropdown.options[Dropdown.value].text : InputField.text; 68 | } 69 | 70 | private void OnDropdownButton() 71 | { 72 | Dropdown.Show(); 73 | } 74 | 75 | private void OnDropdownSelected() 76 | { 77 | UpdatePlaceholder(); 78 | } 79 | 80 | private void OnInputClick() 81 | { 82 | InputButton.gameObject.SetActive(false); 83 | InputField.gameObject.SetActive(true); 84 | InputField.Select(); 85 | InputField.ActivateInputField(); 86 | } 87 | 88 | private void OnInputFieldEdit() 89 | { 90 | UpdatePlaceholder(); 91 | } 92 | 93 | private void OnInputFieldEditEnd() 94 | { 95 | InputField.gameObject.SetActive(false); 96 | InputButton.gameObject.SetActive(true); 97 | 98 | if (!EventSystem.current.alreadySelecting) // could be, in same click, ending edit and starting dropdown 99 | { 100 | EventSystem.current.SetSelectedGameObject(GameObject); // return focus to regular UI 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Settings/Settings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityModManagerNet; 4 | 5 | namespace EnhancedInventory.Settings 6 | { 7 | [Flags] 8 | public enum InventorySearchCriteria 9 | { 10 | ItemName = 1 << 0, 11 | ItemType = 1 << 1, 12 | ItemSubtype = 1 << 2, 13 | ItemDescription = 1 << 3, 14 | 15 | Default = ItemName | ItemType | ItemSubtype 16 | } 17 | 18 | [Flags] 19 | public enum SpellbookSearchCriteria 20 | { 21 | SpellName = 1 << 0, 22 | SpellDescription = 1 << 1, 23 | SpellSaves = 1 << 2, 24 | SpellSchool = 1 << 3, 25 | 26 | Default = SpellName | SpellSaves | SpellSchool, 27 | } 28 | 29 | [Flags] 30 | public enum HighlightLootableOptions 31 | { 32 | UnlearnedScrolls = 1 << 0, 33 | UnlearnedRecipes = 1 << 1, 34 | UnreadDocuments = 1 << 2, 35 | 36 | Default = UnlearnedScrolls | UnlearnedRecipes | UnreadDocuments 37 | } 38 | 39 | [Flags] 40 | public enum FilterCategories 41 | { 42 | NoFilter = 0, 43 | Weapon = 1 << 0, 44 | Armor = 1 << 1, 45 | Accessories = 1 << 2, 46 | Ingredients = 1 << 3, 47 | Usable = 1 << 4, 48 | Notable = 1 << 5, 49 | NonUsable = 1 << 6, 50 | QuickslotUtils = 1 << 7, 51 | UnlearnedScrolls = 1 << 8, 52 | UnlearnedRecipes = 1 << 9, 53 | UnreadDocuments = 1 << 10, 54 | UsableWithoutUMD = 1 << 11, 55 | CurrentEquipped = 1 << 12, 56 | NonZeroPW = 1 << 13, 57 | 58 | Default = Weapon | 59 | Armor | 60 | Accessories | 61 | Ingredients | 62 | Usable | 63 | Notable | 64 | NonUsable | 65 | QuickslotUtils | 66 | UnlearnedScrolls | 67 | UnlearnedRecipes | 68 | UnreadDocuments | 69 | UsableWithoutUMD | 70 | CurrentEquipped | 71 | NonZeroPW, 72 | } 73 | 74 | [Flags] 75 | public enum SorterCategories 76 | { 77 | NotSorted = 0, 78 | TypeUp = 1 << 0, 79 | TypeDown = 1 << 1, 80 | PriceUp = 1 << 2, 81 | PriceDown = 1 << 3, 82 | NameUp = 1 << 4, 83 | NameDown = 1 << 5, 84 | DateUp = 1 << 6, 85 | DateDown = 1 << 7, 86 | WeightUp = 1 << 8, 87 | WeightDown = 1 << 9, 88 | WeightValueUp = 1 << 10, 89 | WeightValueDown = 1 << 11, 90 | 91 | Default = TypeUp | 92 | PriceDown | 93 | DateDown | 94 | WeightDown | 95 | WeightValueUp 96 | } 97 | 98 | // TODO BEFORE RELEASE: Add filter categories 99 | 100 | public class Data : UnityModManager.ModSettings 101 | { 102 | public bool EnableInventorySearchBar = true; 103 | public bool EnableSpellbookSearchBar = true; 104 | public bool EnableHighlightableLoot = true; 105 | public bool EnableVisualOverhaulSorting = true; 106 | public bool EnableCollectAllTweaks = false; 107 | 108 | public bool InventorySearchBarResetFilterWhenOpening = false; 109 | public bool InventorySearchBarFocusWhenOpening = true; 110 | public bool InventorySearchBarScrollResetOnSubmit = true; 111 | public bool InventorySearchBarEnableCategoryButtons = false; 112 | 113 | public bool SpellbookSearchBarFocusWhenOpening = true; 114 | public bool SpellbookShowAllSpellsByDefault = false; 115 | public bool SpellbookShowMetamagicByDefault = true; 116 | public bool SpellbookShowEmptyMetamagicCircles = false; 117 | public bool SpellbookShowLevelWhenViewingAllSpells = true; 118 | public bool SpellbookAutoSwitchToMetamagicTab = false; 119 | 120 | public bool CollectAllZeroWeightItems = true; 121 | public bool CollectAllUnidentifiedItems = true; 122 | public bool CollectAllUsefulItems = true; 123 | public bool CollectAllWeightValue = true; 124 | public float CollectAllWeightValueCutoff = 10.0f; 125 | public bool CollectAllNotableItems = true; 126 | 127 | public InventorySearchCriteria InventorySearchCriteria = InventorySearchCriteria.Default; 128 | public SpellbookSearchCriteria SpellbookSearchCriteria = SpellbookSearchCriteria.Default; 129 | public FilterCategories FilterOptions = FilterCategories.Default; 130 | public SorterCategories SorterOptions = SorterCategories.Default; 131 | public HighlightLootableOptions HighlightLootOptions = HighlightLootableOptions.Default; 132 | 133 | public Color32 HighlightLootBorder = new Color32(255, 215, 0, 255); 134 | public Color32 HighlightLootBackground = new Color32(255, 215, 0, 255); 135 | 136 | public override void Save(UnityModManager.ModEntry modEntry) 137 | { 138 | Save(this, modEntry); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Events/OnAreaLoad.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Controllers; 2 | using Kingmaker; 3 | using Kingmaker.PubSubSystem; 4 | using TMPro; 5 | using UnityEngine; 6 | using UnityEngine.UI; 7 | 8 | namespace EnhancedInventory.Events 9 | { 10 | public class OnAreaLoad : IAreaHandler 11 | { 12 | public void OnAreaDidLoad() 13 | { 14 | Main.RefreshRemappers(); 15 | 16 | if (Main.Settings.EnableInventorySearchBar) 17 | { 18 | LoadInventorySearchBar(); 19 | } 20 | 21 | if (Main.Settings.EnableSpellbookSearchBar) 22 | { 23 | LoadSpellbookSearchBar(); 24 | } 25 | 26 | if (Main.Settings.EnableHighlightableLoot) 27 | { 28 | LoadHighlightLoot(); 29 | } 30 | 31 | if (Main.Settings.EnableVisualOverhaulSorting) 32 | { 33 | SetupSortingStyle(); 34 | } 35 | } 36 | 37 | public void OnAreaBeginUnloading() 38 | { } 39 | 40 | private readonly (string, InventoryType)[] m_inventory_paths = new (string, InventoryType)[] 41 | { 42 | // Regular, in-game inventory. 43 | ("ServiceWindowsPCView/InventoryPCView/Inventory/Stash/StashContainer", InventoryType.InventoryStash), 44 | 45 | // World map inventory. 46 | ("ServiceWindowsConfig/InventoryPCView/Inventory/Stash/StashContainer", InventoryType.InventoryStash), 47 | 48 | // Vendor screen: PC inventory view. 49 | ("VendorPCView/MainContent/PlayerStash", InventoryType.InventoryStash), 50 | 51 | // Vendor screen: Vendor goods view. 52 | ("VendorPCView/MainContent/VendorBlock", InventoryType.Vendor), 53 | 54 | // Shared stash: PC inventory view. 55 | ("LootPCView/Window/Inventory", InventoryType.LootInventoryStash), 56 | 57 | // Shared stash: Stash items view. 58 | ("LootPCView/Window/Collector", InventoryType.LootCollector), 59 | }; 60 | 61 | private void LoadInventorySearchBar() 62 | { 63 | foreach ((string path, InventoryType type) in m_inventory_paths) 64 | { 65 | Transform filters_block_transform = Game.Instance.UI.MainCanvas.transform.Find(path); 66 | if (filters_block_transform != null) 67 | { 68 | filters_block_transform.gameObject.AddComponent().Type = type; 69 | } 70 | } 71 | } 72 | 73 | private void LoadSpellbookSearchBar() 74 | { 75 | string[] paths = new string[] 76 | { 77 | "ServiceWindowsPCView/SpellbookPCView/SpellbookScreen", // game 78 | "ServiceWindowsConfig/SpellbookPCView/SpellbookScreen", // world map 79 | }; 80 | 81 | foreach (string path in paths) 82 | { 83 | Transform spellbook = Game.Instance.UI.MainCanvas.transform.Find(path); 84 | if (spellbook != null) 85 | { 86 | spellbook.gameObject.AddComponent(); 87 | } 88 | } 89 | } 90 | 91 | private void LoadHighlightLoot() 92 | { 93 | Transform stash = Game.Instance.UI.MainCanvas.transform.Find("LootPCView/Window/Collector/Collector/StashScrollView/Viewport/Content"); 94 | if (stash != null) 95 | { 96 | stash.gameObject.AddComponent(); 97 | } 98 | } 99 | 100 | private void SetupSortingStyle() 101 | { 102 | foreach ((string path, InventoryType type) in m_inventory_paths) 103 | { 104 | string viewport_path = $"{path}/{InventoryController.PathToSorter(type)}/Sorting/Dropdown/Template/Viewport"; 105 | Transform viewport = Game.Instance.UI.MainCanvas.transform.Find(viewport_path); 106 | 107 | // This happens if we're on a screen that we don't have access to or screens that have different formatting. 108 | if (viewport == null) continue; 109 | 110 | Transform content = viewport.Find("Content"); 111 | Transform item = content.Find("Item"); 112 | 113 | VerticalLayoutGroup group = content.GetComponent(); 114 | TextMeshProUGUI item_label = item.Find("Item Label").GetComponent(); 115 | RectTransform item_background = item.Find("Item Background").GetComponent(); 116 | RectTransform item_checkmark = item.Find("Item Checkmark").GetComponent(); 117 | RectTransform item_bottom_border = item.Find("BottomBorderImage").GetComponent(); 118 | 119 | group.spacing = 4; 120 | group.padding.top = 0; 121 | group.padding.bottom = 0; 122 | 123 | item_label.fontSize = 16.0f; 124 | item_label.horizontalAlignment = HorizontalAlignmentOptions.Center; 125 | 126 | item_background.anchorMin = new Vector2(0.0f, 0.0f); 127 | item_background.anchorMax = new Vector2(1.0f, 1.0f); 128 | item_background.offsetMin = new Vector2(0.0f, 0.0f); 129 | item_background.offsetMax = new Vector2(0.0f, 0.0f); 130 | 131 | item_checkmark.anchorMin = new Vector2(0.0f, 0.0f); 132 | item_checkmark.anchorMax = new Vector2(1.0f, 1.0f); 133 | item_checkmark.offsetMin = new Vector2(0.0f, 0.0f); 134 | item_checkmark.offsetMax = new Vector2(0.0f, 0.0f); 135 | 136 | item_bottom_border.anchorMin = new Vector2(0.0f, 0.0f); 137 | item_bottom_border.anchorMax = new Vector2(1.0f, 0.0f); 138 | item_bottom_border.offsetMin = new Vector2(0.0f, -2.0f); 139 | item_bottom_border.offsetMax = new Vector2(0.0f, 0.0f); 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Main.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Events; 2 | using EnhancedInventory.Localization; 3 | using EnhancedInventory.Settings; 4 | using EnhancedInventory.Util; 5 | using HarmonyLib; 6 | using Kingmaker.PubSubSystem; 7 | using Kingmaker.UI.Common; 8 | using System.Collections.Generic; 9 | using System.Reflection; 10 | using UnityEngine; 11 | using UnityModManagerNet; 12 | 13 | namespace EnhancedInventory 14 | { 15 | #if DEBUG 16 | [EnableReloading] 17 | #endif 18 | public static class Main 19 | { 20 | public static UnityModManager.ModEntry.ModLogger Logger; 21 | public static Data Settings; 22 | 23 | public static readonly RemappableInt FilterMapper = new RemappableInt(); 24 | public static readonly RemappableInt SorterMapper = new RemappableInt(); 25 | 26 | private static Harmony m_harmony; 27 | private static OnAreaLoad m_area_load_handler; 28 | 29 | public static bool Load(UnityModManager.ModEntry modEntry) 30 | { 31 | Logger = modEntry.Logger; 32 | Settings = Data.Load(modEntry); 33 | 34 | if (ModInterop.HasModConflicts()) 35 | { 36 | Logger.Error("Loading failed due to detected mod conflicts."); 37 | return false; 38 | } 39 | 40 | modEntry.OnGUI = OnGUI; 41 | modEntry.OnSaveGUI = OnSaveGUI; 42 | 43 | #if DEBUG 44 | modEntry.OnUnload = OnUnload; 45 | #endif 46 | 47 | m_harmony = new Harmony(modEntry.Info.Id); 48 | m_harmony.PatchAll(Assembly.GetExecutingAssembly()); 49 | 50 | m_area_load_handler = new OnAreaLoad(); 51 | EventBus.Subscribe(m_area_load_handler); 52 | 53 | return true; 54 | } 55 | 56 | private static void OnGUI(UnityModManager.ModEntry _) 57 | { 58 | GUILayout.Space(4); 59 | SettingsGUI.Draw(); 60 | GUILayout.Space(4); 61 | } 62 | 63 | private static void OnSaveGUI(UnityModManager.ModEntry modEntry) 64 | { 65 | Settings.Save(modEntry); 66 | } 67 | 68 | private static bool OnUnload(UnityModManager.ModEntry _) 69 | { 70 | m_harmony.UnpatchAll(); 71 | EventBus.Unsubscribe(m_area_load_handler); 72 | return true; 73 | } 74 | 75 | public static void RefreshRemappers() 76 | { 77 | FilterMapper.Clear(); 78 | SorterMapper.Clear(); 79 | 80 | foreach (FilterCategories flag in EnumHelper.ValidFilterCategories) 81 | { 82 | if (Settings.FilterOptions.HasFlag(flag) || !Settings.EnableInventorySearchBar) 83 | { 84 | FilterMapper.Add(FilterCategoryMap[flag].Item1); 85 | } 86 | } 87 | 88 | foreach (SorterCategories flag in EnumHelper.ValidSorterCategories) 89 | { 90 | if (Settings.SorterOptions.HasFlag(flag)) 91 | { 92 | SorterMapper.Add(SorterCategoryMap[flag].Item1); 93 | } 94 | } 95 | } 96 | 97 | public static readonly Dictionary FilterCategoryMap = new Dictionary 98 | { 99 | [FilterCategories.NoFilter] = ((int)ItemsFilter.FilterType.NoFilter, null), 100 | [FilterCategories.Weapon] = ((int)ItemsFilter.FilterType.Weapon, null), 101 | [FilterCategories.Armor] = ((int)ItemsFilter.FilterType.Armor, null), 102 | [FilterCategories.Accessories] = ((int)ItemsFilter.FilterType.Accessories, null), 103 | [FilterCategories.Ingredients] = ((int)ItemsFilter.FilterType.Ingredients, null), 104 | [FilterCategories.Usable] = ((int)ItemsFilter.FilterType.Usable, null), 105 | [FilterCategories.Notable] = ((int)ItemsFilter.FilterType.Notable, null), 106 | [FilterCategories.NonUsable] = ((int)ItemsFilter.FilterType.NonUsable, null), 107 | [FilterCategories.QuickslotUtils] = ((int)ExpandedFilterType.QuickslotUtilities, InventoryStrings.QuickslotUtilities), 108 | [FilterCategories.UnlearnedScrolls] = ((int)ExpandedFilterType.UnlearnedScrolls, InventoryStrings.UnlearnedScrolls), 109 | [FilterCategories.UnlearnedRecipes] = ((int)ExpandedFilterType.UnlearnedRecipes, InventoryStrings.UnlearnedRecipes), 110 | [FilterCategories.UnreadDocuments] = ((int)ExpandedFilterType.UnreadDocuments, InventoryStrings.UnreadDocuments), 111 | [FilterCategories.UsableWithoutUMD] = ((int)ExpandedFilterType.UsableWithoutUMD, InventoryStrings.UsableWithoutUMDCheck), 112 | [FilterCategories.CurrentEquipped] = ((int)ExpandedFilterType.CurrentEquipped, InventoryStrings.CurrentEquipped), 113 | [FilterCategories.NonZeroPW] = ((int)ExpandedFilterType.NonZeroPW, InventoryStrings.NonZeroPW), 114 | }; 115 | 116 | public static readonly Dictionary SorterCategoryMap = new Dictionary 117 | { 118 | [SorterCategories.NotSorted] = ((int)ItemsFilter.SorterType.NotSorted, null), 119 | [SorterCategories.TypeUp] = ((int)ItemsFilter.SorterType.TypeUp, null), 120 | [SorterCategories.TypeDown] = ((int)ItemsFilter.SorterType.TypeDown, null), 121 | [SorterCategories.PriceUp] = ((int)ItemsFilter.SorterType.PriceUp, null), 122 | [SorterCategories.PriceDown] = ((int)ItemsFilter.SorterType.PriceDown, null), 123 | [SorterCategories.NameUp] = ((int)ItemsFilter.SorterType.NameUp, null), 124 | [SorterCategories.NameDown] = ((int)ItemsFilter.SorterType.NameDown, null), 125 | [SorterCategories.DateUp] = ((int)ItemsFilter.SorterType.DateUp, null), 126 | [SorterCategories.DateDown] = ((int)ItemsFilter.SorterType.DateDown, null), 127 | [SorterCategories.WeightUp] = ((int)ItemsFilter.SorterType.WeightUp, null), 128 | [SorterCategories.WeightDown] = ((int)ItemsFilter.SorterType.WeightDown, null), 129 | [SorterCategories.WeightValueUp] = ((int)ExpandedSorterType.WeightValueUp, InventoryStrings.PriceWeightAscending), 130 | [SorterCategories.WeightValueDown] = ((int)ExpandedSorterType.WeightValueDown, InventoryStrings.PriceWeightDescending) 131 | }; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Controllers/LootHighlightController.cs: -------------------------------------------------------------------------------- 1 | using Kingmaker.UI.MVVM._PCView.Slots; 2 | using Owlcat.Runtime.UniRx; 3 | using UnityEngine; 4 | using UniRx; 5 | using UnityEngine.EventSystems; 6 | using Kingmaker.UI.Common; 7 | using Kingmaker.UI.MVVM._VM.Slots; 8 | using UnityEngine.UI; 9 | using EnhancedInventory.Settings; 10 | using Kingmaker.Blueprints.Items.Components; 11 | using Kingmaker.Items.Parts; 12 | using Kingmaker.Blueprints; 13 | using Kingmaker; 14 | using System.Linq; 15 | using System; 16 | using Kingmaker.UI.MVVM._PCView.Loot; 17 | using System.Collections.Generic; 18 | 19 | namespace EnhancedInventory.Controllers 20 | { 21 | public class LootHighlightController : MonoBehaviour 22 | { 23 | private List m_handlers = new List(); 24 | private bool m_apply_handlers = true; 25 | private bool m_deferred_update = true; 26 | 27 | private void OnEnable() 28 | { 29 | m_apply_handlers = true; 30 | } 31 | 32 | private void OnTransformChildrenChanged() 33 | { 34 | m_apply_handlers = true; 35 | } 36 | 37 | private void Update() 38 | { 39 | m_deferred_update |= m_apply_handlers; 40 | 41 | ItemSlotsGroupView[] views = m_deferred_update ? GetComponentsInChildren() : null; 42 | 43 | if (m_apply_handlers) 44 | { 45 | foreach (IDisposable handler in m_handlers) 46 | { 47 | handler.Dispose(); 48 | } 49 | 50 | m_handlers.Clear(); 51 | 52 | // Can be null if we're in the loot window with no filters. 53 | ItemsFilterVM filter_vm = GetComponentInParent().m_ItemsFilter.ViewModel; 54 | 55 | if (filter_vm != null) 56 | { 57 | m_handlers.Add(filter_vm.CurrentFilter.Subscribe(delegate { m_deferred_update = true; })); 58 | m_handlers.Add(filter_vm.CurrentSorter.Subscribe(delegate { m_deferred_update = true; })); 59 | } 60 | 61 | foreach (ItemSlotsGroupView view in views) 62 | { 63 | m_handlers.Add(view.ViewModel.CollectionChangedCommand.Subscribe(delegate { m_deferred_update = true; })); 64 | } 65 | 66 | foreach (ItemSlotPCView pc_slot in GetComponentsInChildren()) 67 | { 68 | m_handlers.Add(pc_slot.OnDropAsObservable().Subscribe(delegate { m_deferred_update = true; })); 69 | } 70 | 71 | m_apply_handlers = false; 72 | } 73 | 74 | if (m_deferred_update) 75 | { 76 | foreach (ItemSlotsGroupView view in views) 77 | { 78 | foreach (IWidgetView widget in view.m_WidgetList.m_VisibleEntries) 79 | { 80 | ItemSlotView slot = widget as ItemSlotView; 81 | if (slot == null) continue; 82 | 83 | Transform highlight = slot.transform.Find("Item/EnhancedInventory_Highlight"); 84 | 85 | if (highlight == null) 86 | { 87 | GameObject base_obj = new GameObject("EnhancedInventory_Highlight", new Type[] { typeof(RectTransform), typeof(CanvasGroup) }); 88 | 89 | RectTransform transform = base_obj.GetComponent(); 90 | transform.anchorMin = new Vector2(0.0f, 0.0f); 91 | transform.anchorMax = new Vector2(1.0f, 1.0f); 92 | transform.offsetMin = new Vector2(0.0f, 0.0f); 93 | transform.offsetMax = new Vector2(0.0f, 0.0f); 94 | transform.pivot = new Vector2(0.5f, 0.5f); 95 | 96 | GameObject new_highlight_border = Instantiate(base_obj, base_obj.transform); 97 | new_highlight_border.name = "HighlightBorder"; 98 | new_highlight_border.AddComponent().texture = Texture2D.whiteTexture; 99 | 100 | // Hack to fix bug https://issuetracker.unity3d.com/issues/image-color-cannot-be-changed-via-script-when-image-type-is-set-to-simple 101 | // Duplicating base_obj for HighlightBackground will result in a white, semi-transparent image, but duplicating new_highlight_border 102 | // fixes it, which is why we have done that. 103 | 104 | GameObject new_highlight_bg = Instantiate(new_highlight_border, base_obj.transform); 105 | new_highlight_bg.name = "HighlightBackground"; 106 | new_highlight_bg.transform.localScale = new Vector3(0.9f, 0.9f, 1.0f); 107 | 108 | highlight = base_obj.transform; 109 | highlight.SetParent(slot.transform.Find("Item"), false); 110 | highlight.SetSiblingIndex(1); 111 | } 112 | 113 | highlight.Find("HighlightBorder").GetComponent().color = Main.Settings.HighlightLootBorder; 114 | highlight.Find("HighlightBackground").GetComponent().color = Main.Settings.HighlightLootBackground; 115 | 116 | bool enabled = slot.Item != null; 117 | 118 | if (enabled) 119 | { 120 | CopyScroll scroll = slot.Item.Blueprint.GetComponent(); 121 | CopyRecipe recipe = slot.Item.Blueprint.GetComponent(); 122 | ItemPartShowInfoCallback cb = slot.Item.Get(); 123 | 124 | bool is_copyable_scroll = scroll != null && Game.Instance.Player.Party.Any(i => scroll.CanCopy(slot.Item, i)); 125 | bool is_unlearned_recipe = recipe != null && Game.Instance.Player.Party.Any(i => recipe.CanCopy(slot.Item, i)); 126 | bool is_unread_document = cb != null && (!cb.m_Settings.Once || !cb.m_Triggered); 127 | 128 | enabled = (Main.Settings.HighlightLootOptions.HasFlag(HighlightLootableOptions.UnlearnedScrolls) && is_copyable_scroll) || 129 | (Main.Settings.HighlightLootOptions.HasFlag(HighlightLootableOptions.UnlearnedRecipes) && is_unlearned_recipe) || 130 | (Main.Settings.HighlightLootOptions.HasFlag(HighlightLootableOptions.UnreadDocuments) && is_unread_document); 131 | } 132 | 133 | highlight.gameObject.SetActive(enabled); 134 | } 135 | } 136 | 137 | m_deferred_update = false; 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/* 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | 332 | 333 | -------------------------------------------------------------------------------- /EnhancedInventory/Properties/Resources.zh-CN.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 当前角色可用装备 122 | 123 | 124 | 价格或重量非0 125 | 126 | 127 | 价格 / 重量(升序排列) 128 | 129 | 130 | 价格 / 重量(降序排列) 131 | 132 | 133 | 快捷栏物品(不包括卷轴和药水) 134 | 135 | 136 | 请输入物品名称… 137 | 138 | 139 | 未抄录的食谱 140 | 141 | 142 | 未抄录的卷轴 143 | 144 | 145 | 未阅读的书籍 146 | 147 | 148 | 无需使用魔法装置检定即可使用 149 | 150 | 151 | 抄入{0}个卷轴 152 | 153 | 154 | 请输入法术名称… 155 | 156 | 157 | 强韧 158 | 159 | 160 | 反射 161 | 162 | 163 | 意志 164 | 165 | 166 | 不过滤 167 | 168 | 169 | {0}检定法术 170 | 171 | 172 | 显示全部等级法术 173 | 174 | 175 | 显示超魔法术 176 | 177 | 178 | 显示未学会的法术 179 | 180 | -------------------------------------------------------------------------------- /EnhancedInventory/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Gear for the selected character 122 | 123 | 124 | Non-zero price and weight 125 | 126 | 127 | Price / Weight (in ascending order) 128 | 129 | 130 | Price / Weight (in descending order) 131 | 132 | 133 | Quickslot utilities 134 | 135 | 136 | Enter item name... 137 | 138 | 139 | Unlearned recipes 140 | 141 | 142 | Unlearned scrolls 143 | 144 | 145 | Unread documents 146 | 147 | 148 | Usable without UMD check 149 | 150 | 151 | Learn {0} scrolls 152 | 153 | 154 | Enter spell name... 155 | 156 | 157 | Fortitude 158 | 159 | 160 | Reflex 161 | 162 | 163 | Will 164 | 165 | 166 | No filter 167 | 168 | 169 | Spell targets {0} 170 | 171 | 172 | Show all spell levels 173 | 174 | 175 | Show metamagic 176 | 177 | 178 | Show unlearned spells 179 | 180 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Hooks/ItemsFilter.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Settings; 2 | using EnhancedInventory.Util; 3 | using HarmonyLib; 4 | using Kingmaker.Blueprints; 5 | using Kingmaker.Blueprints.Items; 6 | using Kingmaker.Blueprints.Items.Components; 7 | using Kingmaker.Blueprints.Items.Equipment; 8 | using Kingmaker.EntitySystem.Entities; 9 | using Kingmaker.Items; 10 | using Kingmaker.Items.Parts; 11 | using Kingmaker.UI.Common; 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | namespace EnhancedInventory.Hooks 16 | { 17 | // Hook #1 out of #2 for filtering - this hook handled the custom filter categories. 18 | [HarmonyPatch(typeof(ItemsFilter), nameof(ItemsFilter.ShouldShowItem), new Type[] { typeof(ItemEntity), typeof(ItemsFilter.FilterType) })] 19 | public static class ItemsFilter_ShouldShowItem_ItemEntity 20 | { 21 | // Here, we handle filtering any expanded categories that we have. 22 | [HarmonyPrefix] 23 | public static bool Prefix(ItemEntity item, ItemsFilter.FilterType filter, ref bool __result) 24 | { 25 | ExpandedFilterType expanded_filter = (ExpandedFilterType)filter; 26 | 27 | if (expanded_filter == ExpandedFilterType.QuickslotUtilities) 28 | { 29 | __result = item.Blueprint is BlueprintItemEquipmentUsable blueprint && 30 | blueprint.Type != UsableItemType.Potion && 31 | blueprint.Type != UsableItemType.Scroll; 32 | } 33 | else if (expanded_filter == ExpandedFilterType.UnlearnedScrolls) 34 | { 35 | CopyScroll scroll = item.Blueprint.GetComponent(); 36 | UnitEntityData unit = UIUtility.GetCurrentCharacter(); 37 | __result = scroll != null && unit != null && scroll.CanCopy(item, unit); 38 | } 39 | else if (expanded_filter == ExpandedFilterType.UnlearnedRecipes) 40 | { 41 | CopyRecipe recipe = item.Blueprint.GetComponent(); 42 | __result = recipe != null && recipe.CanCopy(item, null); 43 | } 44 | else if (expanded_filter == ExpandedFilterType.UnreadDocuments) 45 | { 46 | ItemPartShowInfoCallback cb = item.Get(); 47 | __result = cb != null && (!cb.m_Settings.Once || !cb.m_Triggered); 48 | } 49 | else if (expanded_filter == ExpandedFilterType.UsableWithoutUMD) 50 | { 51 | UnitEntityData unit = UIUtility.GetCurrentCharacter(); 52 | __result = item.Blueprint is BlueprintItemEquipmentUsable blueprint && 53 | (blueprint.Type == UsableItemType.Scroll || blueprint.Type == UsableItemType.Wand) && 54 | unit != null && !blueprint.IsUnitNeedUMDForUse(unit); 55 | } 56 | else if (expanded_filter == ExpandedFilterType.CurrentEquipped) 57 | { 58 | UnitEntityData unit = UIUtility.GetCurrentCharacter(); 59 | __result = unit != null; 60 | 61 | if (__result) 62 | { 63 | bool weapon_match = item is ItemEntityWeapon weapon && 64 | ((unit.Body.PrimaryHand.HasWeapon && unit.Body.PrimaryHand.Weapon.Blueprint.Type == weapon.Blueprint.Type) || 65 | (unit.Body.SecondaryHand.HasWeapon && unit.Body.SecondaryHand.Weapon.Blueprint.Type == weapon.Blueprint.Type)); 66 | 67 | bool shield_match = item is ItemEntityShield shield && 68 | ((unit.Body.PrimaryHand.HasShield && unit.Body.PrimaryHand.Shield.Blueprint.Type == shield.Blueprint.Type) || 69 | (unit.Body.SecondaryHand.HasShield && unit.Body.SecondaryHand.Shield.Blueprint.Type == shield.Blueprint.Type)); 70 | 71 | bool armour_match = item is ItemEntityArmor armor && 72 | unit.Body.Armor.HasArmor && unit.Body.Armor.Armor.Blueprint.ProficiencyGroup == armor.Blueprint.ProficiencyGroup; 73 | 74 | __result = weapon_match || shield_match || armour_match; 75 | } 76 | } 77 | else if (expanded_filter == ExpandedFilterType.NonZeroPW) 78 | { 79 | __result = item.Blueprint.SellPrice > 0 && item.Blueprint.Weight > 0.0f; 80 | } 81 | else 82 | { 83 | // Original call - proceed as normal. 84 | return true; 85 | } 86 | 87 | // This call to the blueprint version will skip original in prefix then apply the search bar logic in postfix. 88 | __result = __result && ItemsFilter.ShouldShowItem(item.Blueprint, filter); 89 | return false; 90 | } 91 | } 92 | 93 | // Hook #2 out of #2 for filtering - this hook handles filtering based on string search. 94 | [HarmonyPatch(typeof(ItemsFilter), nameof(ItemsFilter.ShouldShowItem), new Type[] { typeof(BlueprintItem), typeof(ItemsFilter.FilterType) })] 95 | public static class ItemsFilter_ShouldShowItem_Blueprint 96 | { 97 | public static string SearchContents = null; 98 | 99 | // Prefix: If we're filtering one of the expanded categories, we require more than the blueprint - we require the instance. 100 | // If someone calls the function to check the blueprint directly, for expanded categories, we must simply allow everything. 101 | [HarmonyPrefix] 102 | public static bool Prefix(ItemsFilter.FilterType filter, ref bool __result) 103 | { 104 | __result = true; 105 | return (int)filter < (int)ExpandedFilterType.QuickslotUtilities; 106 | } 107 | 108 | // Postfix: We apply the string match, if any, to the resulting matches from the original call (or our prefix). 109 | [HarmonyPostfix] 110 | public static void Postfix(BlueprintItem blueprintItem, ref bool __result) 111 | { 112 | if (__result && !string.IsNullOrWhiteSpace(SearchContents)) 113 | { 114 | __result = false; 115 | 116 | if (Main.Settings.InventorySearchCriteria.HasFlag(InventorySearchCriteria.ItemName)) 117 | { 118 | __result |= blueprintItem.Name.IndexOf(SearchContents, StringComparison.OrdinalIgnoreCase) >= 0; 119 | } 120 | 121 | if (Main.Settings.InventorySearchCriteria.HasFlag(InventorySearchCriteria.ItemType)) 122 | { 123 | __result |= blueprintItem.ItemType.ToString().IndexOf(SearchContents, StringComparison.OrdinalIgnoreCase) >= 0; 124 | } 125 | 126 | if (Main.Settings.InventorySearchCriteria.HasFlag(InventorySearchCriteria.ItemSubtype)) 127 | { 128 | __result |= blueprintItem.SubtypeName.IndexOf(SearchContents, StringComparison.OrdinalIgnoreCase) >= 0; 129 | } 130 | 131 | if (Main.Settings.InventorySearchCriteria.HasFlag(InventorySearchCriteria.ItemDescription)) 132 | { 133 | __result |= blueprintItem.Description.IndexOf(SearchContents, StringComparison.OrdinalIgnoreCase) >= 0; 134 | } 135 | } 136 | } 137 | } 138 | 139 | // Sorting - this hook handles custom sorting categories. 140 | [HarmonyPatch(typeof(ItemsFilter))] 141 | public static class ItemsFilter_ItemSorter 142 | { 143 | private static int CompareByWeightValue(ItemEntity a, ItemEntity b, ItemsFilter.FilterType filter) 144 | { 145 | float a_weight_value = a.Blueprint.Weight <= 0.0f ? float.PositiveInfinity : a.Blueprint.Cost / a.Blueprint.Weight; 146 | float b_weight_value = b.Blueprint.Weight <= 0.0f ? float.PositiveInfinity : b.Blueprint.Cost / b.Blueprint.Weight; 147 | return a_weight_value == b_weight_value ? ItemsFilter.CompareByTypeAndName(a, b, filter) : (a_weight_value > b_weight_value ? 1 : -1); 148 | } 149 | 150 | [HarmonyPrefix] 151 | [HarmonyPatch(nameof(ItemsFilter.CompareByTypeAndName))] 152 | private static bool CompareByTypeAndName(ItemEntity a, ItemEntity b, ItemsFilter.FilterType filter, ref int __result) 153 | { 154 | // First by main type 155 | int a_b_comparison = a.Blueprint.ItemType.CompareTo(b.Blueprint.ItemType); 156 | if (a_b_comparison != 0) 157 | { 158 | __result = a_b_comparison; 159 | return false; 160 | } 161 | 162 | // Then by subtype 163 | a_b_comparison = string.Compare(a.Blueprint.SubtypeName, b.Blueprint.SubtypeName, StringComparison.OrdinalIgnoreCase); 164 | if (a_b_comparison != 0) 165 | { 166 | __result = a_b_comparison; 167 | return false; 168 | } 169 | 170 | // Finally by name 171 | __result = string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase); 172 | return false; 173 | } 174 | 175 | [HarmonyPrefix] 176 | [HarmonyPatch(nameof(ItemsFilter.ItemSorter))] 177 | public static bool Prefix(ItemsFilter.SorterType type, List items, ItemsFilter.FilterType filter, ref List __result) 178 | { 179 | ExpandedSorterType expanded_type = (ExpandedSorterType)type; 180 | 181 | if (expanded_type == ExpandedSorterType.WeightValueUp) 182 | { 183 | items.Sort((ItemEntity a, ItemEntity b) => CompareByWeightValue(a, b, filter)); 184 | } 185 | else if (expanded_type == ExpandedSorterType.WeightValueDown) 186 | { 187 | items.Sort((ItemEntity a, ItemEntity b) => CompareByWeightValue(a, b, filter)); 188 | items.Reverse(); 189 | } 190 | else 191 | { 192 | return true; 193 | } 194 | 195 | __result = items; 196 | return false; 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /EnhancedInventory/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace EnhancedInventory.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EnhancedInventory.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to Gear for the selected character. 65 | /// 66 | internal static string InventoryCurrentEquippedText { 67 | get { 68 | return ResourceManager.GetString("InventoryCurrentEquippedText", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Looks up a localized string similar to Non-zero price and weight. 74 | /// 75 | internal static string InventoryNonZeroPWText { 76 | get { 77 | return ResourceManager.GetString("InventoryNonZeroPWText", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Looks up a localized string similar to Price / Weight (in ascending order). 83 | /// 84 | internal static string InventoryPriceWeightAscendingText { 85 | get { 86 | return ResourceManager.GetString("InventoryPriceWeightAscendingText", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// Looks up a localized string similar to Price / Weight (in descending order). 92 | /// 93 | internal static string InventoryPriceWeightDescendingText { 94 | get { 95 | return ResourceManager.GetString("InventoryPriceWeightDescendingText", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Looks up a localized string similar to Quickslot utilities. 101 | /// 102 | internal static string InventoryQuickslotUtilitiesText { 103 | get { 104 | return ResourceManager.GetString("InventoryQuickslotUtilitiesText", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Looks up a localized string similar to Enter item name.... 110 | /// 111 | internal static string InventorySearchBarText { 112 | get { 113 | return ResourceManager.GetString("InventorySearchBarText", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// Looks up a localized string similar to Unlearned recipes. 119 | /// 120 | internal static string InventoryUnlearnedRecipesText { 121 | get { 122 | return ResourceManager.GetString("InventoryUnlearnedRecipesText", resourceCulture); 123 | } 124 | } 125 | 126 | /// 127 | /// Looks up a localized string similar to Unlearned scrolls. 128 | /// 129 | internal static string InventoryUnlearnedScrollsText { 130 | get { 131 | return ResourceManager.GetString("InventoryUnlearnedScrollsText", resourceCulture); 132 | } 133 | } 134 | 135 | /// 136 | /// Looks up a localized string similar to Unread documents. 137 | /// 138 | internal static string InventoryUnreadDocumentsText { 139 | get { 140 | return ResourceManager.GetString("InventoryUnreadDocumentsText", resourceCulture); 141 | } 142 | } 143 | 144 | /// 145 | /// Looks up a localized string similar to Usable without UMD check. 146 | /// 147 | internal static string InventoryUsableWithoutUMDText { 148 | get { 149 | return ResourceManager.GetString("InventoryUsableWithoutUMDText", resourceCulture); 150 | } 151 | } 152 | 153 | /// 154 | /// Looks up a localized string similar to Learn {0} scrolls. 155 | /// 156 | internal static string SpellbookLearnableScrollsButtonText { 157 | get { 158 | return ResourceManager.GetString("SpellbookLearnableScrollsButtonText", resourceCulture); 159 | } 160 | } 161 | 162 | /// 163 | /// Looks up a localized string similar to Enter spell name.... 164 | /// 165 | internal static string SpellbookSearchBarText { 166 | get { 167 | return ResourceManager.GetString("SpellbookSearchBarText", resourceCulture); 168 | } 169 | } 170 | 171 | /// 172 | /// Looks up a localized string similar to Fortitude. 173 | /// 174 | internal static string SpellbookSearchFilterCheckKeywordFortitudeText { 175 | get { 176 | return ResourceManager.GetString("SpellbookSearchFilterCheckKeywordFortitudeText", resourceCulture); 177 | } 178 | } 179 | 180 | /// 181 | /// Looks up a localized string similar to Reflex. 182 | /// 183 | internal static string SpellbookSearchFilterCheckKeywordReflexText { 184 | get { 185 | return ResourceManager.GetString("SpellbookSearchFilterCheckKeywordReflexText", resourceCulture); 186 | } 187 | } 188 | 189 | /// 190 | /// Looks up a localized string similar to Will. 191 | /// 192 | internal static string SpellbookSearchFilterCheckKeywordWillText { 193 | get { 194 | return ResourceManager.GetString("SpellbookSearchFilterCheckKeywordWillText", resourceCulture); 195 | } 196 | } 197 | 198 | /// 199 | /// Looks up a localized string similar to No filter. 200 | /// 201 | internal static string SpellbookSearchFilterNoFilterText { 202 | get { 203 | return ResourceManager.GetString("SpellbookSearchFilterNoFilterText", resourceCulture); 204 | } 205 | } 206 | 207 | /// 208 | /// Looks up a localized string similar to Spell targets {0}. 209 | /// 210 | internal static string SpellbookSearchFilterTargetsText { 211 | get { 212 | return ResourceManager.GetString("SpellbookSearchFilterTargetsText", resourceCulture); 213 | } 214 | } 215 | 216 | /// 217 | /// Looks up a localized string similar to Show all spell levels. 218 | /// 219 | internal static string SpellbookShowAllSpellLevelsCheckboxText { 220 | get { 221 | return ResourceManager.GetString("SpellbookShowAllSpellLevelsCheckboxText", resourceCulture); 222 | } 223 | } 224 | 225 | /// 226 | /// Looks up a localized string similar to Show metamagic. 227 | /// 228 | internal static string SpellbookShowMetamagicCheckboxText { 229 | get { 230 | return ResourceManager.GetString("SpellbookShowMetamagicCheckboxText", resourceCulture); 231 | } 232 | } 233 | 234 | /// 235 | /// Looks up a localized string similar to Show unlearned spells. 236 | /// 237 | internal static string SpellbookShowUnlearnedSpellsCheckboxText { 238 | get { 239 | return ResourceManager.GetString("SpellbookShowUnlearnedSpellsCheckboxText", resourceCulture); 240 | } 241 | } 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Controllers/InventoryController.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Localization; 2 | using EnhancedInventory.Settings; 3 | using EnhancedInventory.Util; 4 | using Kingmaker; 5 | using Kingmaker.Blueprints.Root; 6 | using Kingmaker.UI; 7 | using Kingmaker.UI.Common; 8 | using Kingmaker.UI.MVVM._PCView.Loot; 9 | using Kingmaker.UI.MVVM._PCView.ServiceWindows.Inventory; 10 | using Kingmaker.UI.MVVM._PCView.Slots; 11 | using Kingmaker.UI.MVVM._PCView.Vendor; 12 | using System; 13 | using System.Collections.Generic; 14 | using UniRx; 15 | using UnityEngine; 16 | using UnityEngine.UI; 17 | 18 | namespace EnhancedInventory.Controllers 19 | { 20 | public enum InventoryType 21 | { 22 | InventoryStash, 23 | Vendor, 24 | LootCollector, 25 | LootInventoryStash 26 | } 27 | 28 | public class InventoryController : MonoBehaviour 29 | { 30 | public InventoryType Type; 31 | 32 | private Transform m_filter_block; 33 | private SearchBar m_search_bar; 34 | private Image[] m_search_icons; 35 | private ReactiveProperty m_active_filter; 36 | private IDisposable m_char_selection_changed_cb; 37 | 38 | private bool m_apply_handlers = true; 39 | private bool m_deferred_update = false; 40 | 41 | private void Awake() 42 | { 43 | m_filter_block = transform.Find(PathToFilterBlock(Type)); 44 | m_search_bar = new SearchBar(m_filter_block, InventoryStrings.EnterItemName); 45 | 46 | m_search_bar.Dropdown.onValueChanged.AddListener(delegate 47 | { 48 | UpdateDropdownIcon(); 49 | m_deferred_update = true; 50 | }); 51 | 52 | m_search_bar.InputField.onValueChanged.AddListener(delegate { m_deferred_update = true; }); 53 | 54 | if (Main.Settings.InventorySearchBarScrollResetOnSubmit) 55 | { 56 | m_search_bar.InputField.onSubmit.AddListener(delegate 57 | { 58 | transform.Find(PathToStashScroll(Type)).GetComponent().value = 0.0f; 59 | }); 60 | } 61 | 62 | m_char_selection_changed_cb = Game.Instance.SelectionCharacter.SelectedUnit.Subscribe(delegate { m_deferred_update = true; }); 63 | 64 | // Add options to the dropdown... 65 | 66 | List options = new List(); 67 | 68 | foreach (FilterCategories flag in EnumHelper.ValidFilterCategories) 69 | { 70 | if (Main.Settings.FilterOptions.HasFlag(flag)) 71 | { 72 | (int idx, string text) = Main.FilterCategoryMap[flag]; 73 | 74 | if (text == null) 75 | { 76 | ItemsFilter.FilterType localization_enum = (ItemsFilter.FilterType)idx; 77 | 78 | // For whatever reason, the localization DB has the wrong info for some of these options... I suspect someone changed the enum order 79 | // around and these particular strings are not used anywhere. 80 | 81 | switch (idx) 82 | { 83 | case (int)ItemsFilter.FilterType.Ingredients: localization_enum = ItemsFilter.FilterType.NonUsable; break; 84 | case (int)ItemsFilter.FilterType.Usable: localization_enum = ItemsFilter.FilterType.Ingredients; break; 85 | case (int)ItemsFilter.FilterType.NonUsable: localization_enum = ItemsFilter.FilterType.Usable; break; 86 | } 87 | 88 | text = LocalizedTexts.Instance.ItemsFilter.GetText(localization_enum); 89 | Main.FilterCategoryMap[flag] = (idx, text); 90 | } 91 | 92 | options.Add(text); 93 | } 94 | } 95 | 96 | m_search_bar.Dropdown.AddOptions(options); 97 | m_search_bar.UpdatePlaceholder(); 98 | 99 | // Gather images for the dropdown... 100 | 101 | List images = new List(); 102 | GameObject switch_bar = m_filter_block.Find("SwitchBar").gameObject; 103 | 104 | foreach (Transform child in switch_bar.transform) 105 | { 106 | images.Add(child.Find("Icon")?.GetComponent()); 107 | } 108 | 109 | while (images.Count < options.Count) 110 | { 111 | images.Add(null); 112 | } 113 | 114 | m_search_icons = images.ToArray(); 115 | 116 | UpdateDropdownIcon(); 117 | 118 | // Tweak positioning depending on user config... 119 | 120 | RectTransform search_transform = m_search_bar.GameObject.GetComponent(); 121 | 122 | if (Main.Settings.InventorySearchBarEnableCategoryButtons) 123 | { 124 | search_transform.localScale = new Vector3(0.6f, 0.6f, 1.0f); 125 | search_transform.localPosition = new Vector3(0.0f, -8.0f, 0.0f); 126 | 127 | RectTransform sb_transform = switch_bar.GetComponent(); 128 | sb_transform.localPosition = new Vector3( 129 | sb_transform.localPosition.x, 130 | sb_transform.localPosition.y + 23.0f, 131 | sb_transform.localPosition.z); 132 | sb_transform.localScale = new Vector3(0.6f, 0.6f, 1.0f); 133 | 134 | // Select the appropriate handler, if possible, in the switch bar, when selected using the dropdown. 135 | m_search_bar.Dropdown.onValueChanged.AddListener(delegate (int idx) 136 | { 137 | if (idx <= (int)ItemsFilter.FilterType.NonUsable) 138 | { 139 | switch_bar.transform.GetChild(idx).GetComponent().ViewModel.IsSelected.Value = true; 140 | } 141 | }); 142 | 143 | // destroy the top and bottom gfx as they cause a lot of noise 144 | Destroy(m_search_bar.GameObject.transform.Find("Background/Decoration/TopLineImage").gameObject); 145 | Destroy(m_search_bar.GameObject.transform.Find("Background/Decoration/BottomLineImage").gameObject); 146 | } 147 | else 148 | { 149 | search_transform.localScale = new Vector3(0.85f, 0.85f, 1.0f); 150 | search_transform.localPosition = new Vector3(0.0f, 2.0f, 0.0f); 151 | Destroy(switch_bar); 152 | } 153 | } 154 | 155 | private void OnEnable() 156 | { 157 | m_apply_handlers = true; 158 | } 159 | 160 | private void OnDestroy() 161 | { 162 | m_char_selection_changed_cb.Dispose(); 163 | } 164 | 165 | private void Update() 166 | { 167 | if (m_apply_handlers) 168 | { 169 | if (Type == InventoryType.InventoryStash) 170 | { 171 | InventoryStashPCView stash_pc_view = GetComponentInParent(); 172 | m_active_filter = stash_pc_view.ViewModel.ItemsFilter.CurrentFilter; 173 | stash_pc_view.ViewModel.ItemSlotsGroup.CollectionChangedCommand.Subscribe(delegate { m_deferred_update = true; }); 174 | stash_pc_view.ViewModel.ItemsFilter.CurrentSorter.Subscribe(delegate { m_deferred_update = true; }); 175 | } 176 | else if (Type == InventoryType.Vendor) 177 | { 178 | VendorPCView vendor_pc_view = GetComponentInParent(); 179 | m_active_filter = vendor_pc_view.ViewModel.VendorItemsFilter.CurrentFilter; 180 | vendor_pc_view.ViewModel.VendorSlotsGroup.CollectionChangedCommand.Subscribe(delegate { m_deferred_update = true; }); 181 | vendor_pc_view.ViewModel.VendorItemsFilter.CurrentSorter.Subscribe(delegate { m_deferred_update = true; }); 182 | } 183 | else if (Type == InventoryType.LootCollector) 184 | { 185 | LootCollectorPCView collector_pc_view = GetComponent(); 186 | m_active_filter = collector_pc_view.ViewModel.ItemsFilter?.CurrentFilter; 187 | collector_pc_view.ViewModel.CollectionChangedCommand.Subscribe(delegate { m_deferred_update = true; }); 188 | 189 | if (m_active_filter != null) // can be null if not on stash view 190 | { 191 | collector_pc_view.ViewModel.ItemsFilter.CurrentSorter.Subscribe(delegate { m_deferred_update = true; }); 192 | } 193 | } 194 | else if (Type == InventoryType.LootInventoryStash) 195 | { 196 | LootInventoryStashPCView inventory_pc_view = GetComponentInParent(); 197 | m_active_filter = inventory_pc_view.ViewModel.ItemsFilter.CurrentFilter; 198 | inventory_pc_view.ViewModel.ItemSlotsGroup.CollectionChangedCommand.Subscribe(delegate { m_deferred_update = true; }); 199 | inventory_pc_view.ViewModel.ItemsFilter.CurrentSorter.Subscribe(delegate { m_deferred_update = true; }); 200 | } 201 | 202 | Transform switch_bar = m_filter_block.Find("SwitchBar"); 203 | 204 | if (switch_bar != null && Type != InventoryType.LootCollector) 205 | { 206 | // Add listeners to each button; if the button changes, we change the dropdown to match. 207 | foreach (ItemsFilter.FilterType filter in Enum.GetValues(typeof(ItemsFilter.FilterType))) 208 | { 209 | int idx = (int)filter; 210 | int mapped_idx = Main.FilterMapper.From(idx); 211 | 212 | if (mapped_idx == -1) 213 | { 214 | switch_bar.transform.GetChild(idx).gameObject.SetActive(false); 215 | } 216 | else 217 | { 218 | ItemsFilterEntityPCView toggle = switch_bar.transform.GetChild(idx).GetComponent(); 219 | toggle.ViewModel.IsSelected.Subscribe(delegate (bool on) { if (on) m_search_bar.Dropdown.value = mapped_idx; } ); 220 | } 221 | } 222 | } 223 | 224 | if (Type == InventoryType.InventoryStash || Type == InventoryType.LootInventoryStash) 225 | { 226 | if (Main.Settings.InventorySearchBarResetFilterWhenOpening) 227 | { 228 | m_search_bar.Dropdown.value = Main.FilterMapper.From((int)ItemsFilter.FilterType.NoFilter); 229 | } 230 | 231 | if (Main.Settings.InventorySearchBarFocusWhenOpening) 232 | { 233 | m_search_bar.FocusSearchBar(); 234 | } 235 | } 236 | 237 | m_apply_handlers = false; 238 | } 239 | 240 | if (m_deferred_update) 241 | { 242 | if (m_active_filter != null) 243 | { 244 | Hooks.ItemsFilter_ShouldShowItem_Blueprint.SearchContents = m_search_bar.InputField.text; 245 | m_active_filter.SetValueAndForceNotify((ItemsFilter.FilterType)Main.FilterMapper.To(m_search_bar.Dropdown.value)); 246 | Hooks.ItemsFilter_ShouldShowItem_Blueprint.SearchContents = null; 247 | } 248 | 249 | m_deferred_update = false; 250 | } 251 | } 252 | 253 | private void UpdateDropdownIcon() 254 | { 255 | m_search_bar.DropdownIconObject.GetComponent().sprite = m_search_icons[m_search_bar.Dropdown.value]?.sprite; 256 | m_search_bar.DropdownIconObject.gameObject.SetActive(m_search_bar.DropdownIconObject.GetComponent().sprite != null); 257 | } 258 | 259 | public static string PathToFilterBlock(InventoryType type) 260 | { 261 | switch (type) 262 | { 263 | case InventoryType.LootCollector: return "Filters/PC_FilterBlock (1)/FilterPCView"; 264 | case InventoryType.LootInventoryStash: return "Filters/PC_FilterBlock/FilterPCView"; 265 | } 266 | 267 | return "PC_FilterBlock/FilterPCView"; 268 | } 269 | 270 | public static string PathToSorter(InventoryType type) 271 | { 272 | string filter = PathToFilterBlock(type); 273 | return filter.Substring(0, filter.LastIndexOf('/')); 274 | } 275 | 276 | public static string PathToStashScroll(InventoryType type) 277 | { 278 | switch (type) 279 | { 280 | case InventoryType.InventoryStash: return "StashScrollView/Scrollbar Vertical"; 281 | case InventoryType.Vendor: return "VendorStashScrollView/Scrollbar Vertical"; 282 | case InventoryType.LootCollector: return "Collector/StashScrollView/Scrollbar Vertical"; 283 | case InventoryType.LootInventoryStash: return "Stash/StashScrollView/Scrollbar Vertical"; 284 | } 285 | 286 | return null; 287 | } 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Settings/SettingsGUI.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Settings; 2 | using EnhancedInventory.Util; 3 | using System; 4 | using System.Collections.Generic; 5 | using UnityEngine; 6 | 7 | namespace EnhancedInventory 8 | { 9 | public static class SettingsGUI 10 | { 11 | private static readonly Dictionary m_visible_state = new Dictionary(); 12 | 13 | public static void Draw() 14 | { 15 | DrawFeatureToggles(); 16 | GUILayout.Space(12); 17 | 18 | if (Main.Settings.EnableInventorySearchBar) 19 | { 20 | DrawInventorySearchBarOptions(); 21 | GUILayout.Space(12); 22 | } 23 | 24 | if (Main.Settings.EnableSpellbookSearchBar) 25 | { 26 | DrawSpellbookSearchBarOptions(); 27 | GUILayout.Space(12); 28 | } 29 | 30 | if (Main.Settings.EnableHighlightableLoot) 31 | { 32 | DrawHighlightableLootOptions(); 33 | GUILayout.Space(12); 34 | } 35 | 36 | if (Main.Settings.EnableCollectAllTweaks) 37 | { 38 | DrawCollectAllOptions(); 39 | GUILayout.Space(12); 40 | } 41 | 42 | DrawSortingCategories(); 43 | } 44 | 45 | private static void DrawFeatureToggles() 46 | { 47 | GUILayout.BeginHorizontal(); 48 | bool draw_features = FeatureButton("Features"); 49 | GUILayout.EndHorizontal(); 50 | 51 | if (!draw_features) return; 52 | 53 | GUILayout.BeginHorizontal(); 54 | Main.Settings.EnableInventorySearchBar = GUILayout.Toggle(Main.Settings.EnableInventorySearchBar, " Enables inventory functionality"); 55 | GUILayout.EndHorizontal(); 56 | 57 | GUILayout.BeginHorizontal(); 58 | Main.Settings.EnableSpellbookSearchBar = GUILayout.Toggle(Main.Settings.EnableSpellbookSearchBar, " Enables spellbook functionality"); 59 | GUILayout.EndHorizontal(); 60 | 61 | GUILayout.BeginHorizontal(); 62 | Main.Settings.EnableHighlightableLoot = GUILayout.Toggle(Main.Settings.EnableHighlightableLoot, " Highlight important items in the loot window"); 63 | GUILayout.EndHorizontal(); 64 | 65 | GUILayout.BeginHorizontal(); 66 | Main.Settings.EnableVisualOverhaulSorting = GUILayout.Toggle(Main.Settings.EnableVisualOverhaulSorting, " Overhaul the visuals of the sort menu"); 67 | GUILayout.EndHorizontal(); 68 | 69 | GUILayout.BeginHorizontal(); 70 | Main.Settings.EnableCollectAllTweaks = GUILayout.Toggle(Main.Settings.EnableCollectAllTweaks, " Tweak the behaviour of the Collect All button"); 71 | GUILayout.EndHorizontal(); 72 | } 73 | 74 | private static void DrawInventorySearchBarOptions() 75 | { 76 | GUILayout.BeginHorizontal(); 77 | bool draw_search_bar = FeatureButton("Inventory"); 78 | GUILayout.EndHorizontal(); 79 | 80 | if (!draw_search_bar) return; 81 | 82 | GUILayout.BeginHorizontal(); 83 | Main.Settings.InventorySearchBarResetFilterWhenOpening = GUILayout.Toggle(Main.Settings.InventorySearchBarResetFilterWhenOpening, " Reset the selected filter when opening the inventory screen"); 84 | GUILayout.EndHorizontal(); 85 | 86 | GUILayout.BeginHorizontal(); 87 | Main.Settings.InventorySearchBarFocusWhenOpening = GUILayout.Toggle(Main.Settings.InventorySearchBarFocusWhenOpening, " Give the search bar focus when opening the inventory screen"); 88 | GUILayout.EndHorizontal(); 89 | 90 | GUILayout.BeginHorizontal(); 91 | Main.Settings.InventorySearchBarScrollResetOnSubmit = GUILayout.Toggle(Main.Settings.InventorySearchBarScrollResetOnSubmit, " When pressing enter to complete a search, the scroll bar will reset to the top of the stash"); 92 | GUILayout.EndHorizontal(); 93 | 94 | GUILayout.BeginHorizontal(); 95 | Main.Settings.InventorySearchBarEnableCategoryButtons = GUILayout.Toggle(Main.Settings.InventorySearchBarEnableCategoryButtons, " Enable hybrid mode; shows the old category buttons above the search bar"); 96 | GUILayout.EndHorizontal(); 97 | 98 | GUILayout.Space(4); 99 | 100 | GUILayout.BeginHorizontal(); 101 | GUILayout.Space(8); 102 | bool draw_criteria = FeatureButton("Inventory Search Criteria", false); 103 | GUILayout.EndHorizontal(); 104 | 105 | if (draw_criteria) 106 | { 107 | InventorySearchCriteria new_options = default; 108 | 109 | foreach (InventorySearchCriteria flag in EnumHelper.ValidInventorySearchCriteria) 110 | { 111 | GUILayout.BeginHorizontal(); 112 | GUILayout.Space(12); 113 | 114 | if (GUILayout.Toggle(Main.Settings.InventorySearchCriteria.HasFlag(flag), $" {flag}")) 115 | { 116 | new_options |= flag; 117 | } 118 | 119 | GUILayout.EndHorizontal(); 120 | } 121 | 122 | Main.Settings.InventorySearchCriteria = new_options; 123 | 124 | GUILayout.Space(4); 125 | } 126 | 127 | GUILayout.BeginHorizontal(); 128 | GUILayout.Space(8); 129 | bool draw_cats = FeatureButton("Enabled Filter Categories", false); 130 | GUILayout.EndHorizontal(); 131 | 132 | if (draw_cats) 133 | { 134 | FilterCategories new_options = FilterCategories.NoFilter; 135 | 136 | foreach (FilterCategories flag in EnumHelper.ValidFilterCategories) 137 | { 138 | if (flag == FilterCategories.NoFilter) continue; 139 | 140 | GUILayout.BeginHorizontal(); 141 | GUILayout.Space(12); 142 | 143 | if (GUILayout.Toggle(Main.Settings.FilterOptions.HasFlag(flag), $" {Main.FilterCategoryMap[flag].Item2 ?? flag.ToString()}")) 144 | { 145 | new_options |= flag; 146 | } 147 | 148 | GUILayout.EndHorizontal(); 149 | } 150 | 151 | Main.Settings.FilterOptions = new_options; 152 | } 153 | } 154 | 155 | private static void DrawSpellbookSearchBarOptions() 156 | { 157 | GUILayout.BeginHorizontal(); 158 | bool draw_search_bar = FeatureButton("Spellbook"); 159 | GUILayout.EndHorizontal(); 160 | 161 | if (!draw_search_bar) return; 162 | 163 | GUILayout.BeginHorizontal(); 164 | Main.Settings.SpellbookSearchBarFocusWhenOpening = GUILayout.Toggle(Main.Settings.SpellbookSearchBarFocusWhenOpening, " Give the search bar focus when opening the spellbook screen"); 165 | GUILayout.EndHorizontal(); 166 | 167 | GUILayout.BeginHorizontal(); 168 | Main.Settings.SpellbookShowAllSpellsByDefault = GUILayout.Toggle(Main.Settings.SpellbookShowAllSpellsByDefault, " Show all spell levels by default"); 169 | GUILayout.EndHorizontal(); 170 | 171 | GUILayout.BeginHorizontal(); 172 | Main.Settings.SpellbookShowMetamagicByDefault = GUILayout.Toggle(Main.Settings.SpellbookShowMetamagicByDefault, " Show metamagic by default"); 173 | GUILayout.EndHorizontal(); 174 | 175 | GUILayout.BeginHorizontal(); 176 | Main.Settings.SpellbookShowEmptyMetamagicCircles = GUILayout.Toggle(Main.Settings.SpellbookShowEmptyMetamagicCircles, " Show the empty grey metamagic circles above spells"); 177 | GUILayout.EndHorizontal(); 178 | 179 | GUILayout.BeginHorizontal(); 180 | Main.Settings.SpellbookShowLevelWhenViewingAllSpells = GUILayout.Toggle(Main.Settings.SpellbookShowLevelWhenViewingAllSpells, " Show level of the spell when the spellbook is showing all spell levels"); 181 | GUILayout.EndHorizontal(); 182 | 183 | GUILayout.BeginHorizontal(); 184 | Main.Settings.SpellbookAutoSwitchToMetamagicTab = GUILayout.Toggle(Main.Settings.SpellbookAutoSwitchToMetamagicTab, " After creating a metamagic spell, switch to the metamagic tab"); 185 | GUILayout.EndHorizontal(); 186 | 187 | GUILayout.Space(4); 188 | 189 | GUILayout.BeginHorizontal(); 190 | GUILayout.Space(8); 191 | bool draw_criteria = FeatureButton("Spellbook Search Criteria", false); 192 | GUILayout.EndHorizontal(); 193 | 194 | if (draw_criteria) 195 | { 196 | SpellbookSearchCriteria new_options = default; 197 | 198 | foreach (SpellbookSearchCriteria flag in EnumHelper.ValidSpellbookSearchCriteria) 199 | { 200 | GUILayout.BeginHorizontal(); 201 | GUILayout.Space(12); 202 | 203 | if (GUILayout.Toggle(Main.Settings.SpellbookSearchCriteria.HasFlag(flag), $" {flag}")) 204 | { 205 | new_options |= flag; 206 | } 207 | 208 | GUILayout.EndHorizontal(); 209 | } 210 | 211 | Main.Settings.SpellbookSearchCriteria = new_options; 212 | 213 | GUILayout.Space(4); 214 | } 215 | } 216 | 217 | private static void DrawCollectAllOptions() 218 | { 219 | GUILayout.BeginHorizontal(); 220 | bool draw_collect_all = FeatureButton("Collect All Tweaks"); 221 | GUILayout.EndHorizontal(); 222 | 223 | if (!draw_collect_all) return; 224 | 225 | GUILayout.BeginHorizontal(); 226 | Main.Settings.CollectAllZeroWeightItems = GUILayout.Toggle(Main.Settings.CollectAllZeroWeightItems, " Collect all zero-weight items"); 227 | GUILayout.EndHorizontal(); 228 | 229 | GUILayout.BeginHorizontal(); 230 | Main.Settings.CollectAllUnidentifiedItems = GUILayout.Toggle(Main.Settings.CollectAllUnidentifiedItems, " Collect all unidentified items"); 231 | GUILayout.EndHorizontal(); 232 | 233 | GUILayout.BeginHorizontal(); 234 | Main.Settings.CollectAllUsefulItems = GUILayout.Toggle(Main.Settings.CollectAllUsefulItems, " Collect all useful (unlearned scrolls/recipes, unread books) items"); 235 | GUILayout.EndHorizontal(); 236 | 237 | GUILayout.BeginHorizontal(); 238 | Main.Settings.CollectAllNotableItems = GUILayout.Toggle(Main.Settings.CollectAllNotableItems, " Collect all notable (golden border) items"); 239 | GUILayout.EndHorizontal(); 240 | 241 | GUILayout.BeginHorizontal(); 242 | Main.Settings.CollectAllWeightValue = GUILayout.Toggle(Main.Settings.CollectAllWeightValue, " Use price / weight cutoff for loot (10 = masterwork weapon) "); 243 | string cutoff = GUILayout.TextField(Main.Settings.CollectAllWeightValueCutoff.ToString(), GUILayout.MinWidth(30)); 244 | GUILayout.FlexibleSpace(); 245 | GUILayout.EndHorizontal(); 246 | 247 | float cutoff_float = 0.0f; 248 | float.TryParse(cutoff, out cutoff_float); 249 | Main.Settings.CollectAllWeightValueCutoff = cutoff_float; 250 | } 251 | 252 | private static void DrawHighlightableLootOptions() 253 | { 254 | GUILayout.BeginHorizontal(); 255 | bool draw_highlight = FeatureButton("Highlight Important Loot"); 256 | GUILayout.EndHorizontal(); 257 | 258 | if (!draw_highlight) return; 259 | 260 | GUILayout.BeginHorizontal(); 261 | GUILayout.Space(8); 262 | 263 | GUILayout.BeginVertical(); 264 | GUILayout.Label("Border Colour "); 265 | GUILayout.Label("Background Colour "); 266 | GUILayout.EndVertical(); 267 | 268 | GUILayout.BeginVertical(); 269 | GUILayout.Label(" Red"); 270 | GUILayout.Label(" Red"); 271 | GUILayout.EndVertical(); 272 | 273 | GUILayout.BeginVertical(); 274 | string border_r_str = GUILayout.TextField(Main.Settings.HighlightLootBorder.r.ToString(), GUILayout.MinWidth(30)); 275 | string bg_r_str = GUILayout.TextField(Main.Settings.HighlightLootBackground.r.ToString(), GUILayout.MinWidth(30)); 276 | GUILayout.EndVertical(); 277 | 278 | GUILayout.BeginVertical(); 279 | GUILayout.Label(" Green"); 280 | GUILayout.Label(" Green"); 281 | GUILayout.EndVertical(); 282 | 283 | GUILayout.BeginVertical(); 284 | string border_g_str = GUILayout.TextField(Main.Settings.HighlightLootBorder.g.ToString(), GUILayout.MinWidth(30)); 285 | string bg_g_str = GUILayout.TextField(Main.Settings.HighlightLootBackground.g.ToString(), GUILayout.MinWidth(30)); 286 | GUILayout.EndVertical(); 287 | GUILayout.BeginVertical(); 288 | GUILayout.Label(" Blue"); 289 | GUILayout.Label(" Blue"); 290 | GUILayout.EndVertical(); 291 | 292 | GUILayout.BeginVertical(); 293 | string border_b_str = GUILayout.TextField(Main.Settings.HighlightLootBorder.b.ToString(), GUILayout.MinWidth(30)); 294 | string bg_b_str = GUILayout.TextField(Main.Settings.HighlightLootBackground.b.ToString(), GUILayout.MinWidth(30)); 295 | GUILayout.EndVertical(); 296 | 297 | GUILayout.FlexibleSpace(); 298 | GUILayout.EndHorizontal(); 299 | GUILayout.Space(2); 300 | 301 | uint.TryParse(border_r_str, out uint border_r); 302 | uint.TryParse(border_g_str, out uint border_g); 303 | uint.TryParse(border_b_str, out uint border_b); 304 | 305 | uint.TryParse(bg_r_str, out uint bg_r); 306 | uint.TryParse(bg_g_str, out uint bg_g); 307 | uint.TryParse(bg_b_str, out uint bg_b); 308 | 309 | Main.Settings.HighlightLootBorder = new Color32( 310 | (byte)Math.Max(0, Math.Min(255, border_r)), 311 | (byte)Math.Max(0, Math.Min(255, border_g)), 312 | (byte)Math.Max(0, Math.Min(255, border_b)), 313 | 255); 314 | 315 | Main.Settings.HighlightLootBackground = new Color32( 316 | (byte)Math.Max(0, Math.Min(255, bg_r)), 317 | (byte)Math.Max(0, Math.Min(255, bg_g)), 318 | (byte)Math.Max(0, Math.Min(255, bg_b)), 319 | 255); 320 | 321 | GUILayout.BeginHorizontal(); 322 | GUILayout.Space(8); 323 | bool draw_highlight_cats = FeatureButton("Highlight Categories", false); 324 | GUILayout.EndHorizontal(); 325 | 326 | if (!draw_highlight_cats) return; 327 | 328 | HighlightLootableOptions new_options = default; 329 | 330 | foreach (HighlightLootableOptions flag in EnumHelper.ValidHighlightLootableOptions) 331 | { 332 | GUILayout.BeginHorizontal(); 333 | GUILayout.Space(12); 334 | 335 | if (GUILayout.Toggle(Main.Settings.HighlightLootOptions.HasFlag(flag), $" {flag}")) 336 | { 337 | new_options |= flag; 338 | } 339 | 340 | GUILayout.EndHorizontal(); 341 | } 342 | 343 | Main.Settings.HighlightLootOptions = new_options; 344 | } 345 | 346 | private static void DrawSortingCategories() 347 | { 348 | GUILayout.BeginHorizontal(); 349 | bool draw_cats = FeatureButton("Enabled Sorting Categories"); 350 | GUILayout.EndHorizontal(); 351 | 352 | if (!draw_cats) return; 353 | 354 | SorterCategories new_options = SorterCategories.NotSorted; 355 | 356 | foreach (SorterCategories flag in EnumHelper.ValidSorterCategories) 357 | { 358 | if (flag == SorterCategories.NotSorted || flag == SorterCategories.Default) continue; 359 | 360 | GUILayout.BeginHorizontal(); 361 | 362 | if (GUILayout.Toggle(Main.Settings.SorterOptions.HasFlag(flag), $" {Main.SorterCategoryMap[flag].Item2 ?? flag.ToString()}")) 363 | { 364 | new_options |= flag; 365 | } 366 | 367 | GUILayout.EndHorizontal(); 368 | } 369 | 370 | Main.Settings.SorterOptions = new_options; 371 | } 372 | 373 | private static bool FeatureButton(string text, bool initial_state = true) 374 | { 375 | GUIStyle base_style = new GUIStyle(GUI.skin.GetStyle("Label")); 376 | base_style.fontStyle = FontStyle.Bold; 377 | base_style.normal.textColor = Color.white; 378 | 379 | if (!m_visible_state.ContainsKey(text)) 380 | { 381 | m_visible_state[text] = initial_state; 382 | } 383 | 384 | bool prev_state = m_visible_state[text]; 385 | string sign = prev_state ? "-" : "+"; 386 | 387 | bool new_state = GUILayout.Button($"{sign} {text}", base_style) ? !prev_state : prev_state; 388 | m_visible_state[text] = new_state; 389 | 390 | return new_state; 391 | } 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /EnhancedInventory/src/Controllers/SpellbookController.cs: -------------------------------------------------------------------------------- 1 | using EnhancedInventory.Util; 2 | using Kingmaker.UI.MVVM._PCView.ServiceWindows.Spellbook; 3 | using Kingmaker.UI.MVVM._PCView.ServiceWindows.Spellbook.KnownSpells; 4 | using Owlcat.Runtime.UniRx; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using UnityEngine; 9 | using UniRx; 10 | using Kingmaker.UnitLogic.Abilities; 11 | using Kingmaker.UI.Common; 12 | using Kingmaker.UI.MVVM._VM.ServiceWindows.Spellbook.KnownSpells; 13 | using Kingmaker.UnitLogic; 14 | using Owlcat.Runtime.UI.Controls.Other; 15 | using Owlcat.Runtime.UI.Controls.Button; 16 | using Kingmaker.UI.MVVM._VM.ServiceWindows.Spellbook.Metamagic; 17 | using HarmonyLib; 18 | using Kingmaker.UI; 19 | using TMPro; 20 | using UnityEngine.UI; 21 | using Kingmaker.UI.MVVM._VM.ServiceWindows.Spellbook.Switchers; 22 | using Kingmaker.UnitLogic.Abilities.Blueprints; 23 | using Kingmaker.UI.MVVM._PCView.ServiceWindows.CharacterInfo.Menu; 24 | using Kingmaker.Items; 25 | using Kingmaker.Blueprints.Items.Components; 26 | using Kingmaker.EntitySystem.Entities; 27 | using Kingmaker.Blueprints; 28 | using Kingmaker.Blueprints.Root; 29 | using Kingmaker.EntitySystem.Stats; 30 | using EnhancedInventory.Localization; 31 | 32 | namespace EnhancedInventory.Controllers 33 | { 34 | public class DummyKnownSpellsView : SpellbookKnownSpellsPCView 35 | { 36 | public override void BindViewImplementation() { } 37 | public override void DestroyViewImplementation() { } 38 | } 39 | 40 | public class SpellbookController : MonoBehaviour 41 | { 42 | private SearchBar m_search_bar; 43 | 44 | private IReactiveProperty m_spellbook; 45 | private IReactiveProperty m_spellbook_level; 46 | private IReactiveProperty m_selected_spell; 47 | 48 | private ScrollRectExtended m_scroll_bar; 49 | 50 | private SpellbookKnownSpellPCView m_known_spell_prefab; 51 | private SpellbookSpellPCView m_possible_spell_prefab; 52 | 53 | private ToggleWorkaround m_all_spells_checkbox; 54 | private ToggleWorkaround m_possible_spells_checkbox; 55 | private ToggleWorkaround m_metamagic_checkbox; 56 | private Button m_learn_scrolls_button; 57 | 58 | private string m_localized_fort; 59 | private string m_localized_reflex; 60 | private string m_localized_will; 61 | 62 | private List m_handlers = new List(); 63 | private bool m_deferred_update = true; 64 | 65 | private int m_last_spell_level = -1; 66 | 67 | private void Awake() 68 | { 69 | m_search_bar = new SearchBar(transform.Find("MainContainer"), SpellbookStrings.EnterSpellName); 70 | m_search_bar.GameObject.transform.localScale = new Vector3(0.85f, 0.85f, 1.0f); 71 | m_search_bar.GameObject.transform.localPosition = new Vector2(-61.0f, 386.0f); 72 | m_search_bar.DropdownIconObject.SetActive(false); 73 | m_search_bar.Dropdown.onValueChanged.AddListener(delegate { m_deferred_update = true; m_scroll_bar.ScrollToTop(); }); 74 | m_search_bar.InputField.onValueChanged.AddListener(delegate { m_deferred_update = true; m_scroll_bar.ScrollToTop(); }); 75 | 76 | // Setup string options... 77 | 78 | m_localized_fort = LocalizedTexts.Instance.Stats.Entries.First(i => i.Stat == StatType.SaveFortitude).Text; 79 | m_localized_reflex = LocalizedTexts.Instance.Stats.Entries.First(i => i.Stat == StatType.SaveReflex).Text; 80 | m_localized_will = LocalizedTexts.Instance.Stats.Entries.First(i => i.Stat == StatType.SaveWill).Text; 81 | 82 | List options = Enum.GetValues(typeof(SpellbookFilter)).Cast().Select(i => i.ToString()).ToList(); 83 | options[(int)SpellbookFilter.NoFilter] = SpellbookStrings.NoFilter; 84 | options[(int)SpellbookFilter.TargetsFortitude] = string.Format(SpellbookStrings.FilterTargets, m_localized_fort); 85 | options[(int)SpellbookFilter.TargetsReflex] = string.Format(SpellbookStrings.FilterTargets, m_localized_reflex); 86 | options[(int)SpellbookFilter.TargetsWill] = string.Format(SpellbookStrings.FilterTargets, m_localized_will); 87 | m_search_bar.Dropdown.AddOptions(options); 88 | m_search_bar.UpdatePlaceholder(); 89 | 90 | // The scroll bar is used for resetting the scroll. 91 | m_scroll_bar = transform.Find("MainContainer/KnownSpells/StandardScrollView").GetComponent(); 92 | 93 | Transform known_spells_transform = transform.Find("MainContainer/KnownSpells"); 94 | 95 | // Grab what we need from the old view then destroy it. 96 | SpellbookKnownSpellsPCView old_view = known_spells_transform.GetComponent(); 97 | m_known_spell_prefab = old_view.m_KnownSpellView; 98 | m_possible_spell_prefab = old_view.m_PossibleSpellView; 99 | Destroy(old_view); 100 | 101 | // Make a dummy view that does nothing - we handle the logic in here. 102 | DummyKnownSpellsView dummy = known_spells_transform.gameObject.AddComponent(); 103 | dummy.m_KnownSpellView = m_known_spell_prefab; 104 | dummy.m_PossibleSpellView = m_possible_spell_prefab; 105 | GetComponentInParent().m_KnownSpellsView = dummy; 106 | 107 | // Disable the current spell level indicator, it isn't used any more. 108 | Destroy(transform.Find("MainContainer/Information/CurrentLevel").gameObject); 109 | 110 | // Create button to toggle metamagic. 111 | GameObject all_spells_button = Instantiate(transform.Find("MainContainer/KnownSpells/Toggle").gameObject, transform.Find("MainContainer/KnownSpells")); 112 | all_spells_button.name = "ToggleAllSpells"; 113 | all_spells_button.transform.localPosition = new Vector2(501.0f, -405.0f); 114 | all_spells_button.transform.Find("Label").GetComponent().text = SpellbookStrings.ShowAllSpellLevels; 115 | m_all_spells_checkbox = all_spells_button.GetComponent(); 116 | m_all_spells_checkbox.onValueChanged.AddListener(delegate { m_deferred_update = true; m_scroll_bar.ScrollToTop(); }); 117 | m_all_spells_checkbox.isOn = Main.Settings.SpellbookShowAllSpellsByDefault; 118 | 119 | GameObject metamagic_button = Instantiate(transform.Find("MainContainer/KnownSpells/Toggle").gameObject, transform.Find("MainContainer/KnownSpells")); 120 | metamagic_button.name = "ToggleMetamagic"; 121 | metamagic_button.transform.localPosition = new Vector2(501.0f, -480.0f); 122 | metamagic_button.transform.Find("Label").GetComponent().text = SpellbookStrings.ShowMetamagic; 123 | m_metamagic_checkbox = metamagic_button.GetComponent(); 124 | m_metamagic_checkbox.onValueChanged.AddListener(delegate { m_deferred_update = true; m_scroll_bar.ScrollToTop(); }); 125 | m_metamagic_checkbox.isOn = Main.Settings.SpellbookShowMetamagicByDefault; 126 | 127 | GameObject possible_spells_button = Instantiate(transform.Find("MainContainer/KnownSpells/Toggle").gameObject, transform.Find("MainContainer/KnownSpells")); 128 | possible_spells_button.name = "TogglePossibleSpells"; 129 | possible_spells_button.transform.localPosition = new Vector2(501.0f, -443.0f); 130 | possible_spells_button.transform.Find("Label").GetComponent().text = SpellbookStrings.ShowUnlearnedSpells; 131 | m_possible_spells_checkbox = possible_spells_button.GetComponent(); 132 | m_possible_spells_checkbox.onValueChanged.AddListener(delegate { m_deferred_update = true; m_scroll_bar.ScrollToTop(); }); 133 | 134 | // Hide original; keep it around for mod interop. 135 | transform.Find("MainContainer/KnownSpells/Toggle").gameObject.SetActive(false); 136 | 137 | // Move the levels display (which is still used for displaying memorized spells). 138 | Transform levels = transform.Find("MainContainer/Levels"); 139 | levels.GetComponent().childAlignment = TextAnchor.MiddleLeft; 140 | levels.localPosition = new Vector2(739.0f, 385.0f); 141 | 142 | // Shamelessly steal a button from the inventory and repurpose it for our nefarious deeds. 143 | GameObject learn_spells_object = Instantiate(transform.parent.parent.Find("CharacterInfoPCView/CharacterScreen/Menu/Button").gameObject, transform.Find("MainContainer")); 144 | learn_spells_object.name = "LearnAllSpells"; 145 | learn_spells_object.transform.localPosition = new Vector2(800.0f, -430.0f); 146 | 147 | Transform existing_bg = learn_spells_object.transform.Find("ButtonBackground"); 148 | learn_spells_object.AddComponent().sprite = existing_bg.GetComponent().sprite; 149 | 150 | RectTransform rect = learn_spells_object.GetComponent(); 151 | rect.sizeDelta = new Vector2(150.0f, 60.0f); 152 | 153 | Destroy(existing_bg.gameObject); 154 | Destroy(learn_spells_object.transform.Find("Selected").gameObject); 155 | Destroy(learn_spells_object.GetComponent()); 156 | Destroy(learn_spells_object.GetComponent()); 157 | 158 | m_learn_scrolls_button = learn_spells_object.AddComponent