diff --git a/StackSplit-Redux/API.cs b/StackSplit-Redux/API.cs index 69e1bcb..0e1273c 100644 --- a/StackSplit-Redux/API.cs +++ b/StackSplit-Redux/API.cs @@ -1,6 +1,13 @@ using System; using StackSplitRedux.MenuHandlers; +using Microsoft.Xna.Framework; + +using StardewModdingAPI; + +using StardewValley; +using StardewValley.Menus; + namespace StackSplitRedux { public class API : IStackSplitAPI @@ -22,5 +29,48 @@ public bool TryRegisterMenu(Type menuType) { Log.Error($"API: Don't know how to handle {menuType}, not registered!"); return false; } + + private static Func CastFunc(Func func) where T : IClickableMenu where O : class { + return menu => menu is T tobj ? func(tobj) : null; + } + + private static Func CastFunc(Func func) where T : IClickableMenu where O : class { + return (menu, other) => menu is T tobj ? func(tobj, other) : null; + } + + public bool RegisterBasicMenu( + Func inventoryGetter, + Func> hoveredItemFieldGetter, + Func> heldItemFieldGetter, + Func>> stackChecker + ) where T : IClickableMenu { + return RegisterBasicMenu( + typeof(T), + CastFunc(inventoryGetter), + CastFunc(hoveredItemFieldGetter), + CastFunc(heldItemFieldGetter), + CastFunc(stackChecker) + ); + + } + + public bool RegisterBasicMenu( + Type menuType, + Func inventoryGetter, + Func> hoveredItemFieldGetter, + Func> heldItemFieldGetter, + Func>> stackChecker + ) { + ModdedAdapterMapping.Add( + menuType, + new ModdedMenuAdapter(inventoryGetter, hoveredItemFieldGetter, heldItemFieldGetter, stackChecker) + ); + + HandlerMapping.Add(menuType, typeof(ModdedMenuHandler)); + + Log.Debug($"API: Registered {menuType} with custom handlers."); + + return true; + } } } diff --git a/StackSplit-Redux/IStackSplitReduxAPI.cs b/StackSplit-Redux/IStackSplitReduxAPI.cs index 1d7b12f..f7cbfd8 100644 --- a/StackSplit-Redux/IStackSplitReduxAPI.cs +++ b/StackSplit-Redux/IStackSplitReduxAPI.cs @@ -1,9 +1,73 @@ using System; +using Microsoft.Xna.Framework; + +using StardewValley; +using StardewValley.Menus; + +using StardewModdingAPI; + namespace StackSplitRedux { - interface IStackSplitAPI + public interface IStackSplitAPI { bool TryRegisterMenu(Type menuType); + + /// + /// Register a new menu type with StackSplitRedux with support for + /// an InventoryMenu, along with support for arbitrary clicks via + /// callbacks. + /// + /// If inventoryGetter and hoveredItemFieldGetter both exist and return + /// non-null values, SSR will initialize its standard inventory handler + /// support. + /// + /// stackChecker is called with the menu instance and a Point representing + /// the mouse coordinates when the user triggers SSR. If your func returns + /// null, nothing will happen. + /// + /// The return tuple contains the initial value to display, and an Action + /// to be called when the user confirms the value they want. The action + /// will be called with a success boolean and the user's input as an int. + /// + /// + /// + /// RegisterBasicMenu( + /// typeof(MyMenu), + /// menu => (menu as MyMenu)?.inventory, + /// menu => menu is MyMenu ? Mod.Helper.Reflection.GetField(menu, "hoverItem") : null, + /// null, + /// (menu, point) => { + /// if (menu is not MyMenu mm) + /// return null; + /// if (!mm.CanSplit(point.X, point.Y)) + /// return null; + /// return new Tuple>(5, (success, amount) => { + /// mm.PerformSplit(point.X, point.Y, amount); + /// }); + /// } + /// ); + /// + /// + /// The class of the menu to be handled + /// Function to get a menu's child InventoryMenu + /// Function to get a menu's hovered item field + /// Function to get a menu's held item field. If this is null, Game1.player.CursorSlotItem will be used instead. + /// + bool RegisterBasicMenu( + Func inventoryGetter, + Func> hoveredItemFieldGetter, + Func> heldItemFieldGetter, + Func>> stackChecker + ) where T : IClickableMenu; + + bool RegisterBasicMenu( + Type menuType, + Func inventoryGetter, + Func> hoveredItemFieldGetter, + Func> heldItemFieldGetter, + Func>> stackChecker + ); + } } diff --git a/StackSplit-Redux/MenuHandlers/BaseMenuHandler.cs b/StackSplit-Redux/MenuHandlers/BaseMenuHandler.cs index 4835b61..7850d49 100644 --- a/StackSplit-Redux/MenuHandlers/BaseMenuHandler.cs +++ b/StackSplit-Redux/MenuHandlers/BaseMenuHandler.cs @@ -232,7 +232,7 @@ protected virtual void InitInventory() { var inventoryMenu = Mod.Reflection.GetField(this.NativeMenu, "inventory").GetValue() as InventoryMenu; var hoveredItemField = Mod.Reflection.GetField(this.NativeMenu, "hoveredItem"); - this.InvHandler.Init(inventoryMenu, hoveredItemField); + this.InvHandler.Init(inventoryMenu, hoveredItemField, null); } catch (Exception e) { Log.Error($"[{nameof(BaseMenuHandler)}.{nameof(InitInventory)}] Failed to initialize the inventory handler. Exception:\n{e}"); diff --git a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/CraftingPageHandler.cs b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/CraftingPageHandler.cs index ff37d28..f23efa1 100644 --- a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/CraftingPageHandler.cs +++ b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/CraftingPageHandler.cs @@ -30,8 +30,9 @@ public override void InitInventory() { // We need to do this explicitly because the crafting page uses a different variable name for hover item. var inventoryMenu = this.MenuPage.inventory; var hoveredItemField = Mod.Reflection.GetField(this.MenuPage, "hoverItem"); + var heldItemField = Mod.Reflection.GetField(this.MenuPage, "heldItem"); - this.InventoryHandler.Init(inventoryMenu, hoveredItemField); + this.InventoryHandler.Init(inventoryMenu, hoveredItemField, heldItemField); } /// Tells the handler that the inventory was shift-clicked. diff --git a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuHandler.cs b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuHandler.cs index 3ceddde..eff57b1 100644 --- a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuHandler.cs +++ b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuHandler.cs @@ -17,6 +17,8 @@ public class GameMenuHandler : BaseMenuHandler { GameMenu.craftingTab, new CraftingPageHandler() } }; + private readonly ModdedPageHandler ModdedPageHandler = new(); + /// The handler for the current tab, private IGameMenuPageHandler CurrentPageHandler = null; @@ -124,19 +126,34 @@ private bool ChangeTabs(int newTab) { CloseCurrentHandler(); - if (!PageHandlers.TryGetValue(newTab, out IGameMenuPageHandler pageHandler)) { - // Please note that this is NOT AN ERROR - Log.TraceIfD($"[{nameof(GameMenuHandler)}.{nameof(ChangeTabs)}] No handler for tab {newTab}"); - return false; + var pages = this.NativeMenu.pages; + var page = pages[newTab]; + + IGameMenuPageHandler pageHandler = null; + + // Before using a built-in handler, try checking to see + // if there's a modded adapter handler. + + if (ModdedAdapterMapping.TryGetAdapter(page.GetType(), out IModdedMenuAdapter adapter)) { + Log.TraceIfD($"[{nameof(GameMenuHandler)}.{nameof(ChangeTabs)}] Found a modded adapter for tab {newTab} : {page.GetType().FullName}"); + + pageHandler = ModdedPageHandler; } - Log.TraceIfD($"[{nameof(GameMenuHandler)}.{nameof(ChangeTabs)}] Found a handler for tab {newTab} : {pageHandler}"); + if (pageHandler == null) { + if (!PageHandlers.TryGetValue(newTab, out pageHandler)) { + // Please note that this is NOT AN ERROR + Log.TraceIfD($"[{nameof(GameMenuHandler)}.{nameof(ChangeTabs)}] No handler for tab {newTab}"); + return false; + } + + Log.TraceIfD($"[{nameof(GameMenuHandler)}.{nameof(ChangeTabs)}] Found a handler for tab {newTab} : {pageHandler}"); + } this.PreviousTab = newTab; this.CurrentPageHandler = pageHandler; - var pages = this.NativeMenu.pages; - pageHandler.Open(this.NativeMenu, pages[newTab], this.InvHandler); + pageHandler.Open(this.NativeMenu, page, this.InvHandler); return true; } diff --git a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuPageHandler.cs b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuPageHandler.cs index a30e08c..8b80438 100644 --- a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuPageHandler.cs +++ b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/GameMenuPageHandler.cs @@ -60,7 +60,7 @@ public virtual void InitInventory() { $"[{nameof(GameMenuPageHandler)}.{nameof(InitInventory)}] Initializing InventoryHandler " + $"for menu = {inventoryMenu}, hovered = {hoveredItemField}" ); - this.InventoryHandler.Init(inventoryMenu, hoveredItemField); + this.InventoryHandler.Init(inventoryMenu, hoveredItemField, null); } catch (Exception e) { Log.Error($"[{nameof(GameMenuPageHandler)}.{nameof(InitInventory)}] Failed to initialize the inventory handler:\n{e}"); diff --git a/StackSplit-Redux/MenuHandlers/GameMenuHandlers/ModdedPageHandler.cs b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/ModdedPageHandler.cs new file mode 100644 index 0000000..47d0608 --- /dev/null +++ b/StackSplit-Redux/MenuHandlers/GameMenuHandlers/ModdedPageHandler.cs @@ -0,0 +1,77 @@ +using System; + +using Microsoft.Xna.Framework; + +using StardewValley; +using StardewValley.Menus; + + +namespace StackSplitRedux.MenuHandlers + { + public class ModdedPageHandler : GameMenuPageHandler + { + + private Action StackCallback; + + private IModdedMenuAdapter ModdedHandler; + + private bool WasInventoryClicked = false; + + public ModdedPageHandler() + : base() { + } + + public override void Open(IClickableMenu menu, IClickableMenu page, InventoryHandler inventoryHandler) { + + ModdedAdapterMapping.TryGetAdapter(page.GetType(), out ModdedHandler); + HasInventory = + ModdedHandler?.GetInventoryMenu(page) != null && + ModdedHandler?.GetHoverItemField(page) != null; + + base.Open(menu, page, inventoryHandler); + } + + public override void InitInventory() { + try { + var inventoryMenu = ModdedHandler?.GetInventoryMenu(MenuPage); + var hoverItem = ModdedHandler?.GetHoverItemField(MenuPage); + var heldItem = ModdedHandler?.GetHeldItemField(MenuPage); + + this.InventoryHandler.Init(inventoryMenu, hoverItem, heldItem); + } + catch (Exception ex) { + Log.Error($"[{nameof(ModdedPageHandler)}:{MenuPage.GetType().Name}.{nameof(InitInventory)}] Failed to initialize the inventory handler:\n{ex}"); + } + } + + public override EInputHandled InventoryClicked(out int stackAmount) { + WasInventoryClicked = true; + return base.InventoryClicked(out stackAmount); + } + + public override EInputHandled OpenSplitMenu(out int stackAmount) { + this.WasInventoryClicked = false; + + var wanted = ModdedHandler?.GetStack(MenuPage, new Point(Game1.getOldMouseX(true), Game1.getOldMouseY(true))); + + if (wanted != null) { + stackAmount = wanted.Item1; + StackCallback = wanted.Item2; + return EInputHandled.Consumed; + } + + stackAmount = 0; + return EInputHandled.NotHandled; + } + + public override void OnStackAmountEntered(int amount) { + if (this.WasInventoryClicked) { + base.OnStackAmountEntered(amount); + return; + } + + StackCallback?.Invoke(true, amount); + StackCallback = null; + } + } + } diff --git a/StackSplit-Redux/MenuHandlers/IModdedMenuAdapter.cs b/StackSplit-Redux/MenuHandlers/IModdedMenuAdapter.cs new file mode 100644 index 0000000..80706fa --- /dev/null +++ b/StackSplit-Redux/MenuHandlers/IModdedMenuAdapter.cs @@ -0,0 +1,21 @@ +using System; +using Microsoft.Xna.Framework; +using StardewValley; +using StardewValley.Menus; +using StardewModdingAPI; + +namespace StackSplitRedux.MenuHandlers + { + public interface IModdedMenuAdapter + { + + InventoryMenu GetInventoryMenu(IClickableMenu menu); + + IReflectedField GetHoverItemField(IClickableMenu menu); + + IReflectedField GetHeldItemField(IClickableMenu menu); + + Tuple> GetStack(IClickableMenu menu, Point mouse); + + } + } diff --git a/StackSplit-Redux/MenuHandlers/InventoryHandler.cs b/StackSplit-Redux/MenuHandlers/InventoryHandler.cs index 164bb11..45bedb7 100644 --- a/StackSplit-Redux/MenuHandlers/InventoryHandler.cs +++ b/StackSplit-Redux/MenuHandlers/InventoryHandler.cs @@ -36,16 +36,36 @@ public class InventoryHandler /// Currently hovered item in the inventory. private Item HoveredItem; + private Item HeldItem { + get { + if (this.HeldItemField == null) + return Game1.player.CursorSlotItem; + return this.HeldItemField.GetValue(); + } + set { + if (this.HeldItemField == null) + Game1.player.CursorSlotItem = value; + else + this.HeldItemField.SetValue(value); + } + } + + /// + /// The held item field owned by the parent menu that contains the inventory, if the menu does not use the global held item. + /// + private IReflectedField HeldItemField; + /// Null constructor that currently only invokes the base null constructor public InventoryHandler() { } /// This must be called everytime the inventory is opened/resized. /// Native inventory. - public void Init(InventoryMenu inventoryMenu, IReflectedField hoveredItemField) { + public void Init(InventoryMenu inventoryMenu, IReflectedField hoveredItemField, IReflectedField heldItemField) { Debug.Assert(inventoryMenu is not null); this.NativeInventoryMenu = inventoryMenu; this.HoveredItemField = hoveredItemField; + this.HeldItemField = heldItemField; // Create the bounds around the inventory var first = this.Inventory[0].bounds; @@ -88,7 +108,7 @@ public bool CanSplitSelectedItem() { Debug.Assert(this.Initialized); var hoveredItem = this.HoveredItem; - var heldItem = Game1.player.CursorSlotItem; + var heldItem = this.HeldItem; if (hoveredItem is null) return false; if (hoveredItem.Stack <= 1) return false; @@ -105,7 +125,7 @@ public void SplitSelectedItem(int stackAmount) { var hoveredItemCount = this.HoveredItem.Stack; // Grab & hold the value int maxStack = hoveredItem.maximumStackSize(); - var heldItem = Game1.player.CursorSlotItem; + var heldItem = this.HeldItem; var heldItemCount = heldItem?.Stack ?? 0; // Grab & hold the value // Run native click code to get the selected item @@ -133,7 +153,7 @@ public void SplitSelectedItem(int stackAmount) { heldItem.Stack = heldItemCount; // Update the native fields - Game1.player.CursorSlotItem = heldItem; + this.HeldItem = heldItem; // Null it out now that we're done with this operation this.HoveredItem = null; diff --git a/StackSplit-Redux/MenuHandlers/ModdedMenuAdapter.cs b/StackSplit-Redux/MenuHandlers/ModdedMenuAdapter.cs new file mode 100644 index 0000000..743cbd9 --- /dev/null +++ b/StackSplit-Redux/MenuHandlers/ModdedMenuAdapter.cs @@ -0,0 +1,48 @@ +using System; + +using Microsoft.Xna.Framework; + +using StardewModdingAPI; + +using StardewValley; +using StardewValley.Menus; + +namespace StackSplitRedux.MenuHandlers + { + public class ModdedMenuAdapter : IModdedMenuAdapter + { + + private readonly Func InventoryGetter; + private readonly Func> HoverItemFieldGetter; + private readonly Func> HeldItemFieldGetter; + private readonly Func>> StackChecker; + + public ModdedMenuAdapter( + Func inventoryGetter, + Func> hoverItemFieldGetter, + Func> heldItemFieldGetter, + Func>> stackChecker) + { + this.InventoryGetter = inventoryGetter; + this.HoverItemFieldGetter = hoverItemFieldGetter; + this.HeldItemFieldGetter = heldItemFieldGetter; + this.StackChecker = stackChecker; + } + + public IReflectedField GetHoverItemField(IClickableMenu menu) { + return HoverItemFieldGetter?.Invoke(menu); + } + + public IReflectedField GetHeldItemField(IClickableMenu menu) { + return HeldItemFieldGetter?.Invoke(menu); + } + + public InventoryMenu GetInventoryMenu(IClickableMenu menu) { + return InventoryGetter?.Invoke(menu); + } + + public Tuple> GetStack(IClickableMenu menu, Point mouse) { + return StackChecker?.Invoke(menu, mouse); + } + } + } diff --git a/StackSplit-Redux/MenuHandlers/ModdedMenuHandler.cs b/StackSplit-Redux/MenuHandlers/ModdedMenuHandler.cs new file mode 100644 index 0000000..195fa8f --- /dev/null +++ b/StackSplit-Redux/MenuHandlers/ModdedMenuHandler.cs @@ -0,0 +1,51 @@ +using StardewValley.Menus; +using StackSplitRedux.UI; + +namespace StackSplitRedux.MenuHandlers + { + public class ModdedMenuHandler : BaseMenuHandler + { + + private readonly ModdedPageHandler ModdedPageHandler = new(); + + public ModdedMenuHandler() + : base() { + } + + public override void Open(IClickableMenu menu) { + base.Open(menu); + this.ModdedPageHandler.Open(menu, this.NativeMenu, this.InvHandler); + } + + public override void Close() { + base.Close(); + this.ModdedPageHandler.Close(); + } + + protected override EInputHandled InventoryClicked() { + var handled = this.ModdedPageHandler.InventoryClicked(out int stackAmount); + if (handled != EInputHandled.NotHandled) + this.SplitMenu = new StackSplitMenu(OnStackAmountReceived, stackAmount); + return handled; + } + + protected override EInputHandled OpenSplitMenu() { + var handled = this.ModdedPageHandler.OpenSplitMenu(out int stackAmount); + if (handled != EInputHandled.NotHandled) + this.SplitMenu = new StackSplitMenu(OnStackAmountReceived, stackAmount); + return handled; + } + + protected override void OnStackAmountReceived(string s) { + if (int.TryParse(s, out int amount)) { + this.ModdedPageHandler.OnStackAmountEntered(amount); + } + base.OnStackAmountReceived(s); + } + + protected override void InitInventory() { + // Do nothing: ModdedPageHandler.Open will init the inventory. + } + + } + } diff --git a/StackSplit-Redux/ModdedAdapterMapping.cs b/StackSplit-Redux/ModdedAdapterMapping.cs new file mode 100644 index 0000000..17d1a72 --- /dev/null +++ b/StackSplit-Redux/ModdedAdapterMapping.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using StackSplitRedux.MenuHandlers; +using StardewModdingAPI; + +namespace StackSplitRedux + { + internal static class ModdedAdapterMapping + { + private static readonly Dictionary AdapterByType = new(); + + internal static void Add(Type menuType, IModdedMenuAdapter adapter) { + if (AdapterByType.ContainsKey(menuType)) + Log.Warn($"Redefining adapter for {menuType}"); + AdapterByType[menuType] = adapter; + } + + internal static bool TryGetAdapter(Type menuType, out IModdedMenuAdapter adapter) { + if (AdapterByType.TryGetValue(menuType, out adapter)) return true; + foreach (var kvp in AdapterByType) { + if (menuType.IsSubclassOf(kvp.Key)) { + adapter = kvp.Value; + return true; + } + } + return false; + } + + } + + } diff --git a/StackSplit-Redux/Properties/AssemblyInfo.cs b/StackSplit-Redux/Properties/AssemblyInfo.cs deleted file mode 100644 index 7fb3850..0000000 --- a/StackSplit-Redux/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("StackSplitX-Redux")] -[assembly: AssemblyDescription("StackSplitX Redux -- specify how many items to split from stack")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("pepoluan")] -[assembly: AssemblyProduct("StackSplitX-Redux")] -[assembly: AssemblyCopyright("Copyright © 2021 pepoluan")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("41f0250e-92c8-4b4a-9685-0f82870f9c23")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.14.3.24")] -[assembly: AssemblyFileVersion("0.14.3.24")] diff --git a/StackSplit-Redux/StackSplit-Redux.csproj b/StackSplit-Redux/StackSplit-Redux.csproj index a272e41..bbca535 100644 --- a/StackSplit-Redux/StackSplit-Redux.csproj +++ b/StackSplit-Redux/StackSplit-Redux.csproj @@ -1,96 +1,15 @@  - - - - 9.0 - Debug - AnyCPU - {41F0250E-92C8-4B4A-9685-0F82870F9C23} - Library - Properties - StackSplitRedux - StackSplit-Redux - v4.5.2 - 512 - true - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + StackSplit-Redux + StackSplitRedux + 0.15.0-unofficial.1-khloeleclair + net5.0 + latest + + + + + + \ No newline at end of file diff --git a/StackSplit-Redux/manifest.json b/StackSplit-Redux/manifest.json index df640db..4fb2c45 100644 --- a/StackSplit-Redux/manifest.json +++ b/StackSplit-Redux/manifest.json @@ -1,12 +1,14 @@ { "Name": "Stack Split Redux", "Author": "pepoluan, tstaples (original idea)", - "Version": "0.14.3", + "Version": "0.15.0-unofficial.1-khloeleclair", "Description": "Custom stack splitting, originally known as StackSplitX", "UniqueID": "pepoluan.StackSplitRedux", "EntryDll": "StackSplit-Redux.dll", - "MinimumApiVersion": "3.8.0", - "UpdateKeys": [ "Nexus:8967" ], + "MinimumApiVersion": "3.13.0", + "UpdateKeys": [ + "Nexus:8967" + ], "Meta:Repository": "https://github.com/pepoluan/StackSplitRedux", "Meta:License": "MIT", "Meta:ChangeLog": { @@ -23,6 +25,7 @@ "0.14.0": "Automatically cap number to MaxPurchasable", "0.14.1": "BuyAction.CanPerformAction now uses MaxPurchasable instead of simple item.sellPrice", "0.14.2": "Enable animation", - "0.14.3": "React properly to .emergencyShutdown" + "0.14.3": "React properly to .emergencyShutdown", + "0.15.0": "Bugfix for crafting pages. Expand API. Update to .NET 5." } -} +} \ No newline at end of file diff --git a/StackSplit-Redux/packages.config b/StackSplit-Redux/packages.config deleted file mode 100644 index 3727d0d..0000000 --- a/StackSplit-Redux/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file