From bb26880f899f914b2fbd0dafe4c63f2e9bc6a84f Mon Sep 17 00:00:00 2001 From: NotLe0n Date: Sat, 17 Apr 2021 21:39:43 +0200 Subject: [PATCH] v1.0 --- BetterChests.cs | 51 ------- BetterChests.sln | 12 +- ILEdits.cs | 40 ------ build.txt | 2 +- description.txt | 18 ++- src/BetterChests.cs | 88 ++++++++++++ src/ConfirmationUI.cs | 37 +++++ src/ILEdits.cs | 82 +++++++++++ BetterChestsUI.cs => src/SortOptionsUI.cs | 63 +++++---- src/UIBetterTextBox.cs | 163 ++++++++++++++++++++++ UITextOption.cs => src/UITextOption.cs | 24 ++-- 11 files changed, 444 insertions(+), 136 deletions(-) delete mode 100644 BetterChests.cs delete mode 100644 ILEdits.cs create mode 100644 src/BetterChests.cs create mode 100644 src/ConfirmationUI.cs create mode 100644 src/ILEdits.cs rename BetterChestsUI.cs => src/SortOptionsUI.cs (63%) create mode 100644 src/UIBetterTextBox.cs rename UITextOption.cs => src/UITextOption.cs (70%) diff --git a/BetterChests.cs b/BetterChests.cs deleted file mode 100644 index 7f6ad8e..0000000 --- a/BetterChests.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.Xna.Framework; -using System.Collections.Generic; -using Terraria; -using Terraria.ModLoader; -using Terraria.UI; - -namespace BetterChests -{ - public class BetterChests : Mod - { - internal UserInterface UserInterface; - - public override void Load() - { - if (!Main.dedServ) - { - UserInterface = new UserInterface(); - UserInterface.SetState(new BetterChestsUI()); - } - - ILEdits.Load(); - } - - private GameTime _lastUpdateUiGameTime; - public override void UpdateUI(GameTime gameTime) - { - _lastUpdateUiGameTime = gameTime; - if (Main.LocalPlayer.chest != -1 && BetterChestsUI.visible) - { - UserInterface.Update(gameTime); - } - } - public override void ModifyInterfaceLayers(List layers) - { - int mouseTextIndex = layers.FindIndex(layer => layer.Name.Equals("Vanilla: Mouse Text")); - if (mouseTextIndex != -1) - { - layers.Insert(mouseTextIndex, new LegacyGameInterfaceLayer( - "BetterChests: UI", - delegate - { - if (Main.LocalPlayer.chest != -1 && BetterChestsUI.visible) - { - UserInterface.Draw(Main.spriteBatch, _lastUpdateUiGameTime); - } - return true; - }, InterfaceScaleType.UI)); - } - } - } -} \ No newline at end of file diff --git a/BetterChests.sln b/BetterChests.sln index 9c801a1..95ccef8 100644 --- a/BetterChests.sln +++ b/BetterChests.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31112.23 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterChests", "BetterChests.csproj", "{4AE73323-57DD-4F6E-BA82-91458D43C821}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterChests", "BetterChests.csproj", "{F911D326-3D9A-4EDC-84EF-AA1A5D88709A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4AE73323-57DD-4F6E-BA82-91458D43C821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4AE73323-57DD-4F6E-BA82-91458D43C821}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4AE73323-57DD-4F6E-BA82-91458D43C821}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4AE73323-57DD-4F6E-BA82-91458D43C821}.Release|Any CPU.Build.0 = Release|Any CPU + {F911D326-3D9A-4EDC-84EF-AA1A5D88709A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F911D326-3D9A-4EDC-84EF-AA1A5D88709A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F911D326-3D9A-4EDC-84EF-AA1A5D88709A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F911D326-3D9A-4EDC-84EF-AA1A5D88709A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {4EC07AD4-6ABE-46BC-927C-8BA28E80A370} + SolutionGuid = {53288DF6-7277-4755-B5CE-763F332F828F} EndGlobalSection EndGlobal diff --git a/ILEdits.cs b/ILEdits.cs deleted file mode 100644 index 546a392..0000000 --- a/ILEdits.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Terraria; -using System.Reflection; -using MonoMod.Cil; -using Mono.Cecil.Cil; -using Terraria.UI; -using System.Diagnostics; - -namespace BetterChests -{ - public class ILEdits - { - public static void Load() - { - IL.Terraria.UI.ChestUI.DrawButton += EditButton; - } - - private static void EditButton(ILContext il) - { - var c = new ILCursor(il); - - // The goal of this IL edit is to replace SortChest() with code to open my UI - // IL_0385: br.s IL_038C - // IL_0387: call void Terraria.UI.ItemSorting::SortChest() - // <=== here - - if (!c.TryGotoNext(MoveType.After, i => i.MatchCall("SortChest"))) - return; - - c.Prev.Operand = typeof(ILEdits).GetMethod("OpenUI", BindingFlags.NonPublic | BindingFlags.Static); - } - - private static void OpenUI() - { - BetterChestsUI.visible = !BetterChestsUI.visible; - } - } -} diff --git a/build.txt b/build.txt index 6dbb70d..6cf4375 100644 --- a/build.txt +++ b/build.txt @@ -1,6 +1,6 @@ displayName = Better Chests author = NotLe0n -version = 0.4 +version = 1.0 hideCode = false hideResources = false includeSource = false diff --git a/description.txt b/description.txt index d0eb6d6..fb0963c 100644 --- a/description.txt +++ b/description.txt @@ -1,12 +1,26 @@ -[c/FFE900:This mod adds more options to sort your chests] +[c/FFE900:This mod improves some aspects of vanilla chests] For a Changlog and more information visit my homepage! Contact [c/66EEFF:NotLe0n#7696] on discord if you have any problems. + +Special Thanks to: +[c/66EEFF:direwolf420, darthmorf and jopojelly for a UIElement] + +_________________________ + +[c/FFE900: Changes] +_________________________ + +- "Deposit All" and "Loot all" have a confirmation button now. +- Clicking on "Sort Items" will open a menu with more sort options to the right and a search bar below the chest UI. + _________________________ [c/FFE900: How to use] _________________________ [c/C4C4C4: Simply open a chest and click on "Sort Items".] -[c/C4C4C4: This will open another menu to the right.] \ No newline at end of file +[c/C4C4C4: This will open another menu to the right.] + +[c/C4C4C4: Use the search bar to search for items] \ No newline at end of file diff --git a/src/BetterChests.cs b/src/BetterChests.cs new file mode 100644 index 0000000..8349b9d --- /dev/null +++ b/src/BetterChests.cs @@ -0,0 +1,88 @@ +using Microsoft.Xna.Framework; +using System.Collections.Generic; +using Terraria; +using Terraria.ModLoader; +using Terraria.UI; + +namespace BetterChests.src +{ + public class BetterChests : Mod + { + internal UserInterface SortUserInterface; + internal UserInterface ConfirmationUserInterface; + public static BetterChests instance; + + public override void Load() + { + instance = this; + + if (!Main.dedServ) + { + SortUserInterface = new UserInterface(); + SortUserInterface.SetState(new SortOptionsUI()); + + ConfirmationUserInterface = new UserInterface(); + ConfirmationUserInterface.SetState(new ConfirmationUI()); + } + + ILEdits.Load(); + } + + public override void Unload() + { + instance = null; + + SortUserInterface = null; + ConfirmationUserInterface = null; + + base.Unload(); + } + + private GameTime _lastUpdateUiGameTime; + public override void UpdateUI(GameTime gameTime) + { + _lastUpdateUiGameTime = gameTime; + if (Main.LocalPlayer.chest != -1) + { + if (SortOptionsUI.visible) + { + SortUserInterface.Update(gameTime); + } + + if (ConfirmationUI.visible) + { + ConfirmationUserInterface.Update(gameTime); + } + } + else + { + ConfirmationUI.visible = false; + } + } + + public override void ModifyInterfaceLayers(List layers) + { + int mouseTextIndex = layers.FindIndex(layer => layer.Name.Equals("Vanilla: Mouse Text")); + if (mouseTextIndex != -1) + { + layers.Insert(mouseTextIndex, new LegacyGameInterfaceLayer( + "BetterChests: UI", + delegate + { + if (Main.LocalPlayer.chest != -1) + { + if (SortOptionsUI.visible) + { + SortUserInterface.Draw(Main.spriteBatch, _lastUpdateUiGameTime); + } + if (ConfirmationUI.visible) + { + ConfirmationUserInterface.Draw(Main.spriteBatch, _lastUpdateUiGameTime); + } + } + return true; + }, InterfaceScaleType.UI)); + } + } + } +} \ No newline at end of file diff --git a/src/ConfirmationUI.cs b/src/ConfirmationUI.cs new file mode 100644 index 0000000..64b0c9e --- /dev/null +++ b/src/ConfirmationUI.cs @@ -0,0 +1,37 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Terraria; +using Terraria.GameContent.UI.Elements; +using Terraria.UI; + +namespace BetterChests.src +{ + internal class ConfirmationUI : UIState + { + public static bool visible = false; + public int buttonID; + public float topOffset; + public MouseEvent onclick; + + public override void OnInitialize() + { + var confirmation = new UITextOption("Are you sure?"); + confirmation.TextColor = Color.Red; + confirmation.Top.Set(Main.instance.invBottom + topOffset, 0); + confirmation.Left.Set(506, 0); // magic number because vanilla does it the same way lmao + confirmation.OnMouseDown += onclick; + Append(confirmation); + } + + public override void Update(GameTime gameTime) + { + base.Update(gameTime); + + ChestUI.ButtonScale[buttonID] = 0f; + } + } +} diff --git a/src/ILEdits.cs b/src/ILEdits.cs new file mode 100644 index 0000000..958063a --- /dev/null +++ b/src/ILEdits.cs @@ -0,0 +1,82 @@ +using MonoMod.Cil; +using System.Reflection; +using Terraria.UI; + +namespace BetterChests.src +{ + public class ILEdits + { + public static void Load() + { + IL.Terraria.UI.ChestUI.DrawButton += EditButton; + } + + private static void EditButton(ILContext il) + { + var c = new ILCursor(il); + + if (!c.TryGotoNext(MoveType.After, i => i.MatchCall("LootAll"))) + return; + + c.Prev.Operand = typeof(ILEdits).GetMethod("OpenLootConfirmation", BindingFlags.NonPublic | BindingFlags.Static); + + // IL_0362: br.s IL_038C + // IL_0364: call void Terraria.UI.ChestUI::DepositAll() + // <=== here + + if (!c.TryGotoNext(MoveType.After, i => i.MatchCall("DepositAll"))) + return; + + c.Prev.Operand = typeof(ILEdits).GetMethod("OpenDepositConfirmation", BindingFlags.NonPublic | BindingFlags.Static); + + // The goal of this IL edit is to replace SortChest() with code to open my UI + // IL_0385: br.s IL_038C + // IL_0387: call void Terraria.UI.ItemSorting::SortChest() + // <=== here + + if (!c.TryGotoNext(MoveType.After, i => i.MatchCall("SortChest"))) + return; + + c.Prev.Operand = typeof(ILEdits).GetMethod("ToggleSortUI", BindingFlags.NonPublic | BindingFlags.Static); + } + + private static void ToggleSortUI() + { + SortOptionsUI.visible = !SortOptionsUI.visible; + } + + private static void OpenDepositConfirmation() + { + var ui = new ConfirmationUI + { + buttonID = ChestUI.ButtonID.DepositAll, + topOffset = 55, + onclick = (evt, elm) => + { + ChestUI.DepositAll(); + ConfirmationUI.visible = false; + } + }; + + BetterChests.instance.ConfirmationUserInterface.SetState(ui); + ConfirmationUI.visible = true; + } + + private static void OpenLootConfirmation() + { + var ui = new ConfirmationUI + { + buttonID = ChestUI.ButtonID.LootAll, + topOffset = 30, + onclick = (evt, elm) => + { + ChestUI.LootAll(); + ConfirmationUI.visible = false; + } + }; + + BetterChests.instance.ConfirmationUserInterface.SetState(ui); + ConfirmationUI.visible = true; + } + } +} diff --git a/BetterChestsUI.cs b/src/SortOptionsUI.cs similarity index 63% rename from BetterChestsUI.cs rename to src/SortOptionsUI.cs index 9170524..fca4278 100644 --- a/BetterChestsUI.cs +++ b/src/SortOptionsUI.cs @@ -1,16 +1,15 @@ -using Terraria.GameContent.UI.Elements; -using Terraria.UI; -using Terraria; +using Microsoft.Xna.Framework; +using System; using System.Linq; +using Terraria; +using Terraria.GameContent.UI.Elements; +using Terraria.Graphics; using Terraria.ID; -using System; -using Microsoft.Xna.Framework; -using Terraria.ModLoader; -using Terraria.UI.Chat; +using Terraria.UI; -namespace BetterChests +namespace BetterChests.src { - class BetterChestsUI : UIState + internal class SortOptionsUI : UIState { public static bool visible = false; private bool _reversed = false; @@ -26,25 +25,45 @@ public override void OnInitialize() list = new UIList(); list.Top.Set(Main.instance.invBottom + 30, 0); list.Left.Set(506 + 100, 0); - list.Width.Set(300, 0); + list.Width.Set(200, 0); list.Height.Set(400, 0); list.ListPadding = 14; Append(list); AddSortOption("Default sort", (evt, elm) => ItemSorting.SortChest()); - AddSortOption("Sort by ID", (evt, elm) => Sort(x => x.netID, _reversed)); - AddSortOption("Sort by name", (evt, elm) => Sort(x => x.Name, _reversed)); + AddSortOption("Sort by ID", (evt, elm) => Sort(x => x.type, _reversed)); + AddSortOption("Sort Alphabetically", (evt, elm) => Sort(x => x.Name, _reversed)); AddSortOption("Sort by rarity", (evt, elm) => Sort(x => x.rare, !_reversed)); AddSortOption("Sort by stack size", (evt, elm) => Sort(x => x.stack, !_reversed)); AddSortOption("Sort by value", (evt, elm) => Sort(x => x.value, !_reversed)); AddSortOption("Sort by damage", (evt, elm) => Sort(x => x.damage, !_reversed)); AddSortOption("Sort by defense", (evt, elm) => Sort(x => x.defense, !_reversed)); + AddSortOption("Sort randomly", (evt, elm) => Sort(x => Main.rand.NextFloat(), _reversed)); + + var option = new UITextOption("Reversed: No"); + option.MarginTop = 2f; + option.MarginLeft = 2f; + option.OnClick += (evt, elm) => + { + _reversed = !_reversed; + option.SetText(_reversed ? "Reversed: Yes" : "Reversed: No"); + }; + list.Add(option); + var searchbox = new UIBetterTextBox("search item", Color.White); + searchbox.Top.Set(Main.instance.invBottom + 170, 0); + searchbox.Left.Set(71, 0); + searchbox.Width.Set(209, 0); + searchbox.Height.Set(30, 0); + searchbox.OnTextChanged += () => Sort(x => x.Name.ToLower().Contains(searchbox.currentString.ToLower()), true); + Append(searchbox); } private void AddSortOption(string title, MouseEvent onclick) { var option = new UITextOption(title); + option.MarginTop = 2f; + option.MarginLeft = 2f; option.OnClick += onclick; list.Add(option); } @@ -55,7 +74,7 @@ private void Sort(Func func, bool reversed) ref var items = ref Main.chest[Main.LocalPlayer.chest].item; // order the items according to the function. - var sortedItems = items.OrderBy(func).ToArray(); + var sortedItems = items.OrderBy(func).ThenBy(x => x.type).ToArray(); if (reversed) { @@ -66,15 +85,15 @@ private void Sort(Func func, bool reversed) // Air always goes last sortedItems = sortedItems.OrderBy(x => x.IsAir).ToArray(); - // Change color of changed slots for (int i = 0; i < items.Length; i++) { - if (!items[i].IsAir && items[i] != sortedItems[i]) - { + if (!sortedItems[i].IsAir && items[i] != sortedItems[i]) + { + // Change color of changed slots ItemSlot.SetGlow(i, Main.rand.NextFloat(), true); } } - + // Apply changes items = sortedItems; @@ -87,15 +106,5 @@ private void Sort(Func func, bool reversed) } } } - - public override void Update(GameTime gameTime) - { - base.Update(gameTime); - - if (ContainsPoint(Main.MouseScreen)) - { - Main.LocalPlayer.mouseInterface = true; - } - } } } \ No newline at end of file diff --git a/src/UIBetterTextBox.cs b/src/UIBetterTextBox.cs new file mode 100644 index 0000000..503c7c0 --- /dev/null +++ b/src/UIBetterTextBox.cs @@ -0,0 +1,163 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; +using ReLogic.Graphics; +using System; +using Terraria; +using Terraria.GameContent.UI.Elements; +using Terraria.UI; + +namespace BetterChests.src +{ + //ty jopojelly and darthmorf + internal class UIBetterTextBox : UIPanel + { + internal string currentString = string.Empty; + + internal bool focused = false; + + private readonly int _maxLength = int.MaxValue; + + private readonly string hintText; + private int textBlinkerCount; + private int textBlinkerState; + + public event Action OnFocus; + public event Action OnUnfocus; + public event Action OnTextChanged; + public event Action OnTabPressed; + public event Action OnEnterPressed; + + internal bool unfocusOnEnter = true; + internal bool unfocusOnTab = true; + + public Color TextColor { get; set; } + + internal UIBetterTextBox(string hintText, Color textColor, string text = "") + { + this.hintText = hintText; + currentString = text; + SetPadding(0); + BackgroundColor = new Color(63, 82, 151) * 0.7f; + BorderColor = Color.Black; + TextColor = textColor; + } + + public override void Click(UIMouseEvent evt) + { + Focus(); + base.Click(evt); + } + + internal void Unfocus() + { + if (focused) + { + focused = false; + Main.blockInput = false; + + OnUnfocus?.Invoke(); + } + } + + internal void Focus() + { + if (!focused) + { + Main.clrInput(); + focused = true; + Main.blockInput = true; + + OnFocus?.Invoke(); + } + } + + public override void Update(GameTime gameTime) + { + Vector2 MousePosition = new Vector2(Main.mouseX, Main.mouseY); + if (!ContainsPoint(MousePosition) && (Main.mouseLeft || Main.mouseRight)) //This solution is fine, but we need a way to cleanly "unload" a UIElement + { + //TODO, figure out how to refocus without triggering unfocus while clicking enable button. + Unfocus(); + } + base.Update(gameTime); + } + + internal void SetText(string text) + { + if (text.Length > _maxLength) + { + text = text.Substring(0, _maxLength); + } + if (currentString != text) + { + currentString = text; + OnTextChanged?.Invoke(); + } + } + + private static bool JustPressed(Keys key) + { + return Main.inputText.IsKeyDown(key) && !Main.oldInputText.IsKeyDown(key); + } + + protected override void DrawSelf(SpriteBatch spriteBatch) + { + Rectangle hitbox = GetInnerDimensions().ToRectangle(); + + //Draw panel + base.DrawSelf(spriteBatch); + + if (focused) + { + Terraria.GameInput.PlayerInput.WritingText = true; + Main.instance.HandleIME(); + string newString = Main.GetInputText(currentString); + if (!newString.Equals(currentString)) + { + currentString = newString; + OnTextChanged?.Invoke(); + } + else + { + currentString = newString; + } + + if (JustPressed(Keys.Tab)) + { + if (unfocusOnTab) Unfocus(); + OnTabPressed?.Invoke(); + } + if (JustPressed(Keys.Enter)) + { + Main.drawingPlayerChat = false; + if (unfocusOnEnter) Unfocus(); + OnEnterPressed?.Invoke(); + } + if (++textBlinkerCount >= 20) + { + textBlinkerState = (textBlinkerState + 1) % 2; + textBlinkerCount = 0; + } + Main.instance.DrawWindowsIMEPanel(new Vector2(98f, Main.screenHeight - 36), 0f); + } + string displayString = currentString; + if (textBlinkerState == 1 && focused) + { + displayString += "|"; + } + CalculatedStyle space = GetDimensions(); + Color color = TextColor; + Vector2 drawPos = space.Position() + new Vector2(4, 2); + if (currentString.Length == 0 && !focused) + { + color *= 0.5f; + spriteBatch.DrawString(Main.fontMouseText, hintText, drawPos, color); + } + else + { + spriteBatch.DrawString(Main.fontMouseText, displayString, drawPos, TextColor); + } + } + } +} \ No newline at end of file diff --git a/UITextOption.cs b/src/UITextOption.cs similarity index 70% rename from UITextOption.cs rename to src/UITextOption.cs index 58ebc38..089918b 100644 --- a/UITextOption.cs +++ b/src/UITextOption.cs @@ -1,15 +1,11 @@ -using Microsoft.Xna.Framework.Graphics; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; using Terraria; using Terraria.GameContent.UI.Elements; using Terraria.ID; using Terraria.UI; -namespace BetterChests +namespace BetterChests.src { public class UITextOption : UIText { @@ -17,7 +13,7 @@ public class UITextOption : UIText public bool isLarge; private float _firstTextScale; - public UITextOption(string text, float textScale = 0.85f, bool large = false) : base(text, textScale, large) + public UITextOption(string text, float textScale = 0.75f, bool large = false) : base(text, textScale, large) { _firstTextScale = textScale; TextScale = textScale; @@ -34,7 +30,7 @@ public override void Draw(SpriteBatch spriteBatch) { base.Draw(spriteBatch); - if (IsMouseHovering) + if (IsMouseHovering) { if (TextScale <= 1) { @@ -51,5 +47,15 @@ public override void Draw(SpriteBatch spriteBatch) SetText(Text, TextScale, isLarge); } + + public override void Update(GameTime gameTime) + { + base.Update(gameTime); + + if (ContainsPoint(Main.MouseScreen)) + { + Main.LocalPlayer.mouseInterface = true; + } + } } }