diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java index b19227f2..e4ba56b6 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java @@ -172,7 +172,12 @@ private boolean handleLayoutAction(GuiLayout layout, Player player, SpawnerData } // If no items to sell, still allow exp collection if (spawner.getVirtualInventory().getUsedSlots() == 0) { - boolean success = handleExpBottleAcceptedClick(player, spawner, true); + boolean success = false; + if (spawner.getSpawnerExp() > 0) { + success = tryCollectExpForPlayer(player, spawner); + } else { + messageService.sendMessage(player, "spawner_storage_empty"); + } playActionResult(player, button, clickType, success); return true; } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageAction.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageAction.java index 64567717..6dd57021 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageAction.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageAction.java @@ -227,10 +227,10 @@ private void handleSellAction(Player player, SpawnerData spawner, boolean collec // Check if there are items to sell if (spawner.getVirtualInventory().getUsedSlots() == 0) { - if (collectExp) { - // No items to sell but still collect exp + if (collectExp && spawner.getSpawnerExp() > 0) { + // No items to sell, but collect available exp without leaving the storage GUI boolean success = plugin.getSpawnerMenuAction() - .handleExpBottleAcceptedClick(player, spawner, true); + .tryCollectExpForPlayer(player, spawner); playActionResult(player, sourceButton, sourceClickType, success); } else { messageService.sendMessage(player, "spawner_storage_empty"); diff --git a/core/src/main/java/github/nighter/smartspawner/updates/GuiLayoutUpdater.java b/core/src/main/java/github/nighter/smartspawner/updates/GuiLayoutUpdater.java index 12f33de4..18beb931 100644 --- a/core/src/main/java/github/nighter/smartspawner/updates/GuiLayoutUpdater.java +++ b/core/src/main/java/github/nighter/smartspawner/updates/GuiLayoutUpdater.java @@ -39,6 +39,9 @@ public void checkAndUpdateLayouts() { plugin.getResource(resource), java.util.List.of(), ConfigMigrations.GUI_LAYOUT, + true, + YamlMigrator.OwnedSection.fullyUserManaged( + (defaults, path) -> !path.contains(".") && path.startsWith("slot_")), plugin.getLogger()); } } diff --git a/core/src/main/java/github/nighter/smartspawner/updates/YamlMigrator.java b/core/src/main/java/github/nighter/smartspawner/updates/YamlMigrator.java index b69360cd..f8b9cbdb 100644 --- a/core/src/main/java/github/nighter/smartspawner/updates/YamlMigrator.java +++ b/core/src/main/java/github/nighter/smartspawner/updates/YamlMigrator.java @@ -71,18 +71,22 @@ public interface SectionMatcher { * children back would resurrect deleted drops, or put a chat line back under a message the * owner turned into an action bar.
* - *Either way, once the user has the section its contents are left alone. The two factories + *
Either way, once the user has the section its contents are left alone. The factories * differ only in what an absent section means, which is not the same question for a - * drop list as for a message.
+ * drop list, a message, or a fully user-managed GUI button list. */ - public record OwnedSection(SectionMatcher matcher, boolean keepDeleted) { + public record OwnedSection(SectionMatcher matcher, boolean keepDeleted, boolean alwaysLocked) { + + public OwnedSection(SectionMatcher matcher, boolean keepDeleted) { + this(matcher, keepDeleted, false); + } /** * For a list the user curates. A section they removed entirely stays removed, as long as * they still have its parent. Deleting a mob's whole {@code loot} block has to stick. */ public static OwnedSection curated(SectionMatcher matcher) { - return new OwnedSection(matcher, true); + return new OwnedSection(matcher, true, false); } /** @@ -92,7 +96,15 @@ public static OwnedSection curated(SectionMatcher matcher) { * theirs; only a wholly absent message is refilled. */ public static OwnedSection restoredWhenAbsent(SectionMatcher matcher) { - return new OwnedSection(matcher, false); + return new OwnedSection(matcher, false, false); + } + + /** + * For sections whose presence and contents are both controlled by the user. Matching + * default sections are never topped up, even when the user removed or relocated them. + */ + public static OwnedSection fullyUserManaged(SectionMatcher matcher) { + return new OwnedSection(matcher, true, true); } } @@ -247,14 +259,19 @@ private static int addMissingKeys(YamlConfiguration user, YamlConfiguration defa * leaf of a brand new section create that section, after which every remaining leaf of it looks * user-owned and gets skipped, leaving the section half filled. * - *A section the user has is always owned. A section they do not have is owned only for a - * {@link OwnedSection#curated} marker, and then only when they still have its parent, which is - * what tells a block they deleted apart from one they have never seen.
+ *A section the user has is always owned. A fully user-managed section is also owned when + * absent. Other absent sections are owned only for a {@link OwnedSection#curated} marker, and + * then only when they still have its parent, which tells a block they deleted apart from one + * they have never seen.
*/ private static Set