diff --git a/core/src/main/java/github/nighter/smartspawner/SmartSpawner.java b/core/src/main/java/github/nighter/smartspawner/SmartSpawner.java index 83bafb74..9063cdd1 100644 --- a/core/src/main/java/github/nighter/smartspawner/SmartSpawner.java +++ b/core/src/main/java/github/nighter/smartspawner/SmartSpawner.java @@ -25,6 +25,7 @@ import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuUI; import github.nighter.smartspawner.spawner.gui.main.SpawnerMenuFormUI; import github.nighter.smartspawner.spawner.gui.stacker.SpawnerStackerHandler; +import github.nighter.smartspawner.spawner.gui.stacker.SpawnerStackerFormUI; import github.nighter.smartspawner.spawner.gui.storage.filter.FilterConfigUI; import github.nighter.smartspawner.spawner.gui.synchronization.SpawnerGuiViewManager; import github.nighter.smartspawner.spawner.gui.stacker.SpawnerStackerUI; @@ -89,6 +90,7 @@ public class SmartSpawner extends JavaPlugin implements SmartSpawnerPlugin { private SpawnerStorageUI spawnerStorageUI; private FilterConfigUI filterConfigUI; private SpawnerStackerUI spawnerStackerUI; + private SpawnerStackerFormUI spawnerStackerFormUI; // Core handlers private SpawnEggHandler spawnEggHandler; @@ -250,13 +252,16 @@ private void initializeFormUIComponents() { && integrationManager.getFloodgateHook().isEnabled()) { try { this.spawnerMenuFormUI = new SpawnerMenuFormUI(this); + this.spawnerStackerFormUI = new SpawnerStackerFormUI(this, spawnerStackerHandler); getLogger().info("FormUI components initialized successfully for Bedrock player support"); } catch (NoClassDefFoundError | Exception e) { getLogger().warning("Failed to initialize FormUI components: " + e.getMessage()); this.spawnerMenuFormUI = null; + this.spawnerStackerFormUI = null; } } else { this.spawnerMenuFormUI = null; + this.spawnerStackerFormUI = null; debug("FormUI components not initialized - Floodgate integration not available"); } } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuFormUI.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuFormUI.java index ea2c29da..6bb3269b 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuFormUI.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuFormUI.java @@ -4,6 +4,7 @@ import github.nighter.smartspawner.SmartSpawner; import github.nighter.smartspawner.language.MessageService; import github.nighter.smartspawner.spawner.properties.SpawnerData; +import github.nighter.smartspawner.spawner.properties.VirtualInventory; import github.nighter.smartspawner.language.LanguageManager; import github.nighter.smartspawner.spawner.gui.layout.GuiLayout; import github.nighter.smartspawner.spawner.gui.layout.GuiButton; @@ -15,10 +16,16 @@ import java.util.*; public class SpawnerMenuFormUI { + private static final int TICKS_PER_SECOND = 20; + private final SmartSpawner plugin; private final LanguageManager languageManager; private final MessageService messageService; + // Form cache to avoid rebuilding forms every time + private final Map formCache = new HashMap<>(); + private static final long CACHE_EXPIRY_TIME_MS = 30000; // 30 seconds + // Action to button info mapping private static final Map ACTION_BUTTON_CONFIG = new HashMap<>(); @@ -55,12 +62,17 @@ public SpawnerMenuFormUI(SmartSpawner plugin) { this.messageService = plugin.getMessageService(); } + public void clearCache() { + formCache.clear(); + } + + public void invalidateSpawnerCache(String spawnerId) { + formCache.entrySet().removeIf(entry -> entry.getKey().startsWith(spawnerId + "|")); + } + public void openSpawnerForm(Player player, SpawnerData spawner) { String entityName = languageManager.getFormattedMobName(spawner.getEntityType()); - Map placeholders = new HashMap<>(); - placeholders.put("ᴇɴᴛɪᴛʏ", entityName); - placeholders.put("entity", entityName); - placeholders.put("amount", String.valueOf(spawner.getStackSize())); + Map placeholders = createPlaceholders(player, spawner); String title; if (spawner.getStackSize() > 1) { @@ -77,6 +89,17 @@ public void openSpawnerForm(Player player, SpawnerData spawner) { return; } + // Create cache key based on spawner state + String cacheKey = spawner.getSpawnerId() + "|" + spawner.getStackSize() + "|" + + spawner.getSpawnerExp() + "|" + spawner.getVirtualInventory().getUsedSlots(); + + // Check cache first + CachedForm cachedForm = formCache.get(cacheKey); + if (cachedForm != null && !cachedForm.isExpired() && cachedForm.buttons.equals(availableButtons)) { + FloodgateApi.getInstance().getPlayer(player.getUniqueId()).sendForm(cachedForm.form); + return; + } + SimpleForm.Builder formBuilder = SimpleForm.builder() .title(title); @@ -84,6 +107,12 @@ public void openSpawnerForm(Player player, SpawnerData spawner) { formBuilder.button(buttonInfo.text, FormImage.Type.URL, buttonInfo.imageUrl); } + // Add spawner info as the last button (below action buttons) + String spawnerInfo = createSpawnerInfoContent(player, spawner, placeholders); + if (!spawnerInfo.isEmpty()) { + formBuilder.button(spawnerInfo, FormImage.Type.URL, "https://i.imgur.com/8JlZJGT.png"); // Info icon + } + SimpleForm form = formBuilder .closedOrInvalidResultHandler(() -> { }) @@ -113,9 +142,16 @@ public void openSpawnerForm(Player player, SpawnerData spawner) { break; } }); + } else { + // Info button clicked - reopen the same form + Scheduler.runTask(() -> openSpawnerForm(player, spawner)); } }) .build(); + + // Cache the form + formCache.put(cacheKey, new CachedForm(form, availableButtons)); + FloodgateApi.getInstance().getPlayer(player.getUniqueId()).sendForm(form); } @@ -195,7 +231,21 @@ private void handleSpawnerInfo(Player player, SpawnerData spawner) { messageService.sendMessage(player, "no_permission"); return; } - plugin.getSpawnerStackerUI().openStackerGui(player, spawner); + + // Check if player is Bedrock and use form UI if available + if (isBedrockPlayer(player) && plugin.getSpawnerStackerFormUI() != null) { + plugin.getSpawnerStackerFormUI().openStackerForm(player, spawner); + } else { + plugin.getSpawnerStackerUI().openStackerGui(player, spawner); + } + } + + private boolean isBedrockPlayer(Player player) { + if (plugin.getIntegrationManager() == null || + plugin.getIntegrationManager().getFloodgateHook() == null) { + return false; + } + return plugin.getIntegrationManager().getFloodgateHook().isBedrockPlayer(player); } private void handleSellInventory(Player player, SpawnerData spawner) { @@ -219,6 +269,168 @@ private void handleExpCollection(Player player, SpawnerData spawner) { plugin.getSpawnerMenuAction().handleExpBottleClick(player, spawner, false); } + private Map createPlaceholders(Player player, SpawnerData spawner) { + String entityName = languageManager.getFormattedMobName(spawner.getEntityType()); + String entityNameSmallCaps = languageManager.getSmallCaps(entityName); + + Map placeholders = new HashMap<>(); + placeholders.put("ᴇɴᴛɪᴛʏ", entityNameSmallCaps); + placeholders.put("entity", entityName); + placeholders.put("amount", String.valueOf(spawner.getStackSize())); + placeholders.put("entity_type", spawner.getEntityType().toString()); + + // Stack information + placeholders.put("stack_size", String.valueOf(spawner.getStackSize())); + + // Spawner settings + placeholders.put("range", String.valueOf(spawner.getSpawnerRange())); + long delaySeconds = spawner.getSpawnDelay() / TICKS_PER_SECOND; + placeholders.put("delay", String.valueOf(delaySeconds)); + placeholders.put("delay_raw", String.valueOf(spawner.getSpawnDelay())); + placeholders.put("min_mobs", String.valueOf(spawner.getMinMobs())); + placeholders.put("max_mobs", String.valueOf(spawner.getMaxMobs())); + + // Storage information + VirtualInventory virtualInventory = spawner.getVirtualInventory(); + int currentItems = virtualInventory.getUsedSlots(); + int maxSlots = spawner.getMaxSpawnerLootSlots(); + double percentStorageDecimal = maxSlots > 0 ? ((double) currentItems / maxSlots) * 100 : 0; + String formattedPercentStorage = String.format("%.1f", percentStorageDecimal); + + placeholders.put("current_items", String.valueOf(currentItems)); + placeholders.put("max_items", languageManager.formatNumber(maxSlots)); + placeholders.put("formatted_storage", formattedPercentStorage); + + // Experience information + long currentExp = spawner.getSpawnerExp(); + long maxExp = spawner.getMaxStoredExp(); + double percentExpDecimal = maxExp > 0 ? ((double) currentExp / maxExp) * 100 : 0; + String formattedPercentExp = String.format("%.1f", percentExpDecimal); + + String formattedCurrentExp = languageManager.formatNumber(currentExp); + String formattedMaxExp = languageManager.formatNumber(maxExp); + + placeholders.put("current_exp", formattedCurrentExp); + placeholders.put("max_exp", formattedMaxExp); + placeholders.put("raw_current_exp", String.valueOf(currentExp)); + placeholders.put("raw_max_exp", String.valueOf(maxExp)); + placeholders.put("formatted_exp", formattedPercentExp); + + // Total sell price information + double totalSellPrice = spawner.getAccumulatedSellValue(); + placeholders.put("total_sell_price", languageManager.formatNumber(totalSellPrice)); + + return placeholders; + } + + private String createSpawnerInfoContent(Player player, SpawnerData spawner, Map placeholders) { + // Get the info lines from config + List infoLines = languageManager.getGuiItemLoreAsList("bedrock.main_gui.spawner_info", placeholders); + + if (infoLines == null || infoLines.isEmpty()) { + return ""; // Return empty string if no config + } + + // Convert to Bedrock-compatible color codes and join with newlines + StringBuilder content = new StringBuilder(); + for (String line : infoLines) { + String bedrockLine = convertToBedrockColors(line); + content.append(bedrockLine).append("\n"); + } + + // Remove trailing newline + if (content.length() > 0) { + content.setLength(content.length() - 1); + } + + return content.toString(); + } + + /** + * Converts hex and standard color codes to Bedrock-compatible color codes (§0-§9, §a-§f, §g) + */ + private String convertToBedrockColors(String text) { + if (text == null) return ""; + + // First apply placeholders and convert hex colors to standard Bukkit colors + String result = text; + + // Convert hex patterns like &#RRGGBB to approximate Bedrock colors + result = result.replaceAll("&#([A-Fa-f0-9]{6})", ""); // Remove hex colors for now, will use closest match + + // Map common hex colors to Bedrock equivalents + result = mapHexToBedrockColors(result, text); + + // Convert & color codes to § for Bedrock + result = result.replace("&0", "§0"); + result = result.replace("&1", "§1"); + result = result.replace("&2", "§2"); + result = result.replace("&3", "§3"); + result = result.replace("&4", "§4"); + result = result.replace("&5", "§5"); + result = result.replace("&6", "§6"); + result = result.replace("&7", "§7"); + result = result.replace("&8", "§8"); + result = result.replace("&9", "§9"); + result = result.replace("&a", "§a"); + result = result.replace("&b", "§b"); + result = result.replace("&c", "§c"); + result = result.replace("&d", "§d"); + result = result.replace("&e", "§e"); + result = result.replace("&f", "§f"); + result = result.replace("&g", "§g"); + + return result; + } + + /** + * Maps hex color codes to the closest Bedrock color code + */ + private String mapHexToBedrockColors(String result, String original) { + // Common color mappings from hex to Bedrock + Map colorMap = new HashMap<>(); + + // Grays and blacks + colorMap.put("545454", "§8"); // Dark Gray + colorMap.put("bdc3c7", "§7"); // Gray + colorMap.put("ecf0f1", "§f"); // White + colorMap.put("f8f8ff", "§f"); // White + + // Blues + colorMap.put("3498db", "§9"); // Blue + + // Greens + colorMap.put("2ecc71", "§a"); // Green + colorMap.put("37eb9a", "§a"); // Green + colorMap.put("2cc483", "§a"); // Green + colorMap.put("48e89b", "§a"); // Green + colorMap.put("00F986", "§a"); // Green + + // Reds + colorMap.put("e67e22", "§6"); // Gold + colorMap.put("ff5252", "§c"); // Red + colorMap.put("e63939", "§4"); // Dark Red + colorMap.put("ff7070", "§c"); // Red + + // Purples + colorMap.put("d8c5ff", "§d"); // Light Purple + colorMap.put("7b68ee", "§5"); // Dark Purple + colorMap.put("a885fc", "§d"); // Light Purple + colorMap.put("c2a8fc", "§d"); // Light Purple + colorMap.put("ab7afd", "§d"); // Light Purple + + // Oranges and golds + colorMap.put("EF6C00", "§6"); // Gold + colorMap.put("607D8B", "§8"); // Dark Gray + + for (Map.Entry entry : colorMap.entrySet()) { + result = result.replace("&#" + entry.getKey(), entry.getValue()); + result = result.replace("&#" + entry.getKey().toLowerCase(), entry.getValue()); + } + + return result; + } + private static class ButtonInfo { final String action; final String text; @@ -240,4 +452,20 @@ private static class ActionButtonInfo { this.imageUrl = imageUrl; } } + + private static class CachedForm { + final SimpleForm form; + final List buttons; + final long timestamp; + + CachedForm(SimpleForm form, List buttons) { + this.form = form; + this.buttons = buttons; + this.timestamp = System.currentTimeMillis(); + } + + boolean isExpired() { + return System.currentTimeMillis() - timestamp > CACHE_EXPIRY_TIME_MS; + } + } } \ No newline at end of file diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerFormUI.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerFormUI.java new file mode 100644 index 00000000..2bb19294 --- /dev/null +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerFormUI.java @@ -0,0 +1,286 @@ +package github.nighter.smartspawner.spawner.gui.stacker; + +import github.nighter.smartspawner.Scheduler; +import github.nighter.smartspawner.SmartSpawner; +import github.nighter.smartspawner.language.LanguageManager; +import github.nighter.smartspawner.language.MessageService; +import github.nighter.smartspawner.spawner.properties.SpawnerData; +import org.bukkit.entity.Player; +import org.geysermc.cumulus.form.SimpleForm; +import org.geysermc.cumulus.util.FormImage; +import org.geysermc.floodgate.api.FloodgateApi; + +import java.util.*; + +public class SpawnerStackerFormUI { + private final SmartSpawner plugin; + private final LanguageManager languageManager; + private final MessageService messageService; + private final SpawnerStackerHandler stackerHandler; + + // Form cache to avoid rebuilding forms every time + private final Map formCache = new HashMap<>(); + private static final long CACHE_EXPIRY_TIME_MS = 30000; // 30 seconds + + // Stack amounts for buttons + private static final int[] STACK_AMOUNTS = {1, 10, 64}; + + public SpawnerStackerFormUI(SmartSpawner plugin, SpawnerStackerHandler handler) { + this.plugin = plugin; + this.languageManager = plugin.getLanguageManager(); + this.messageService = plugin.getMessageService(); + this.stackerHandler = handler; + } + + public void clearCache() { + formCache.clear(); + } + + public void invalidateSpawnerCache(String spawnerId) { + formCache.entrySet().removeIf(entry -> entry.getKey().startsWith(spawnerId + "|")); + } + + public void openStackerForm(Player player, SpawnerData spawner) { + String entityName = languageManager.getFormattedMobName(spawner.getEntityType()); + String entityNameSmallCaps = languageManager.getSmallCaps(entityName); + + Map placeholders = new HashMap<>(); + placeholders.put("entity", entityName); + placeholders.put("ᴇɴᴛɪᴛʏ", entityNameSmallCaps); + placeholders.put("stack_size", String.valueOf(spawner.getStackSize())); + placeholders.put("max_stack_size", String.valueOf(spawner.getMaxStackSize())); + + String title = languageManager.getGuiTitle("bedrock.stacker_gui.title", placeholders); + + // Create cache key based on spawner state + String cacheKey = spawner.getSpawnerId() + "|" + spawner.getStackSize() + "|" + spawner.getMaxStackSize(); + + // Check cache first + CachedStackerForm cachedForm = formCache.get(cacheKey); + if (cachedForm != null && !cachedForm.isExpired()) { + FloodgateApi.getInstance().getPlayer(player.getUniqueId()).sendForm(cachedForm.form); + return; + } + + List buttons = new ArrayList<>(); + + // Add decrease buttons + for (int amount : STACK_AMOUNTS) { + String buttonText = languageManager.getGuiItemName("bedrock.stacker_gui.button_decrease", + createAmountPlaceholders(spawner, amount)); + buttons.add(new StackerButtonInfo("decrease", amount, buttonText, + "https://i.imgur.com/kKBt9pj.png")); // Red minus icon + } + + // Add info button + String infoText = createStackerInfoText(spawner, placeholders); + buttons.add(new StackerButtonInfo("info", 0, infoText, + "https://i.imgur.com/8JlZJGT.png")); // Info icon + + // Add increase buttons + for (int amount : STACK_AMOUNTS) { + String buttonText = languageManager.getGuiItemName("bedrock.stacker_gui.button_increase", + createAmountPlaceholders(spawner, amount)); + buttons.add(new StackerButtonInfo("increase", amount, buttonText, + "https://i.imgur.com/L9zVBPL.png")); // Green plus icon + } + + // Add back button + String backText = languageManager.getGuiItemName("bedrock.stacker_gui.button_back", placeholders); + buttons.add(new StackerButtonInfo("back", 0, backText, + "https://i.imgur.com/oTfHJ8P.png")); // Back arrow icon + + SimpleForm.Builder formBuilder = SimpleForm.builder() + .title(title); + + for (StackerButtonInfo buttonInfo : buttons) { + formBuilder.button(buttonInfo.text, FormImage.Type.URL, buttonInfo.imageUrl); + } + + SimpleForm form = formBuilder + .closedOrInvalidResultHandler(() -> { + // Reopen main menu when form is closed + Scheduler.runTask(() -> { + if (plugin.getSpawnerMenuFormUI() != null) { + plugin.getSpawnerMenuFormUI().openSpawnerForm(player, spawner); + } + }); + }) + .validResultHandler(response -> { + int buttonId = response.clickedButtonId(); + if (buttonId < buttons.size()) { + StackerButtonInfo buttonInfo = buttons.get(buttonId); + Scheduler.runTask(() -> { + handleButtonClick(player, spawner, buttonInfo); + }); + } + }) + .build(); + + // Cache the form + formCache.put(cacheKey, new CachedStackerForm(form)); + + FloodgateApi.getInstance().getPlayer(player.getUniqueId()).sendForm(form); + } + + private Map createAmountPlaceholders(SpawnerData spawner, int amount) { + Map placeholders = new HashMap<>(); + String entityName = languageManager.getFormattedMobName(spawner.getEntityType()); + placeholders.put("entity", entityName); + placeholders.put("ᴇɴᴛɪᴛʏ", languageManager.getSmallCaps(entityName)); + placeholders.put("amount", String.valueOf(amount)); + placeholders.put("plural", amount > 1 ? "s" : ""); + placeholders.put("stack_size", String.valueOf(spawner.getStackSize())); + placeholders.put("max_stack_size", String.valueOf(spawner.getMaxStackSize())); + return placeholders; + } + + private String createStackerInfoText(SpawnerData spawner, Map placeholders) { + // Get the info lines from config + List infoLines = languageManager.getGuiItemLoreAsList("bedrock.stacker_gui.info_text", placeholders); + + if (infoLines == null || infoLines.isEmpty()) { + // Fallback to basic info + return "§fStack: §9" + spawner.getStackSize() + " §7/ §9" + spawner.getMaxStackSize(); + } + + // Convert to Bedrock-compatible color codes and join with newlines + StringBuilder content = new StringBuilder(); + for (String line : infoLines) { + String bedrockLine = convertToBedrockColors(line); + content.append(bedrockLine).append("\n"); + } + + // Remove trailing newline + if (content.length() > 0) { + content.setLength(content.length() - 1); + } + + return content.toString(); + } + + private String convertToBedrockColors(String text) { + if (text == null) return ""; + + String result = text; + + // Map common hex colors to Bedrock equivalents + result = mapHexToBedrockColors(result); + + // Convert & color codes to § for Bedrock + result = result.replace("&0", "§0").replace("&1", "§1").replace("&2", "§2").replace("&3", "§3") + .replace("&4", "§4").replace("&5", "§5").replace("&6", "§6").replace("&7", "§7") + .replace("&8", "§8").replace("&9", "§9").replace("&a", "§a").replace("&b", "§b") + .replace("&c", "§c").replace("&d", "§d").replace("&e", "§e").replace("&f", "§f") + .replace("&g", "§g"); + + return result; + } + + private String mapHexToBedrockColors(String result) { + Map colorMap = new HashMap<>(); + + // Grays and blacks + colorMap.put("545454", "§8"); + colorMap.put("bdc3c7", "§7"); + colorMap.put("ecf0f1", "§f"); + colorMap.put("f8f8ff", "§f"); + + // Blues + colorMap.put("3498db", "§9"); + + // Greens + colorMap.put("2ecc71", "§a"); + colorMap.put("37eb9a", "§a"); + colorMap.put("2cc483", "§a"); + colorMap.put("48e89b", "§a"); + colorMap.put("00F986", "§a"); + + // Reds + colorMap.put("e67e22", "§6"); + colorMap.put("ff5252", "§c"); + colorMap.put("e63939", "§4"); + colorMap.put("ff7070", "§c"); + + // Purples + colorMap.put("d8c5ff", "§d"); + colorMap.put("7b68ee", "§5"); + + for (Map.Entry entry : colorMap.entrySet()) { + result = result.replace("&#" + entry.getKey(), entry.getValue()); + result = result.replace("&#" + entry.getKey().toLowerCase(), entry.getValue()); + } + + return result; + } + + private void handleButtonClick(Player player, SpawnerData spawner, StackerButtonInfo buttonInfo) { + switch (buttonInfo.action) { + case "decrease": + // Validate amount to prevent exploits + if (buttonInfo.amount <= 0 || buttonInfo.amount > 64) { + plugin.getLogger().warning("Invalid decrease amount from Bedrock player " + player.getName() + ": " + buttonInfo.amount); + return; + } + stackerHandler.handleStackDecrease(player, spawner, buttonInfo.amount); + // Reopen the form to show updated values + Scheduler.runTaskLater(() -> openStackerForm(player, spawner), 2L); + break; + + case "increase": + // Validate amount to prevent exploits + if (buttonInfo.amount <= 0 || buttonInfo.amount > 64) { + plugin.getLogger().warning("Invalid increase amount from Bedrock player " + player.getName() + ": " + buttonInfo.amount); + return; + } + stackerHandler.handleStackIncrease(player, spawner, buttonInfo.amount); + // Reopen the form to show updated values + Scheduler.runTaskLater(() -> openStackerForm(player, spawner), 2L); + break; + + case "info": + // Just reopen the form (refresh) + openStackerForm(player, spawner); + break; + + case "back": + // Return to main menu + if (plugin.getSpawnerMenuFormUI() != null) { + plugin.getSpawnerMenuFormUI().openSpawnerForm(player, spawner); + } + break; + + default: + plugin.getLogger().warning("Unknown action in StackerFormUI: " + buttonInfo.action); + break; + } + } + + private static class StackerButtonInfo { + final String action; + final int amount; + final String text; + final String imageUrl; + + StackerButtonInfo(String action, int amount, String text, String imageUrl) { + this.action = action; + this.amount = amount; + this.text = text; + this.imageUrl = imageUrl; + } + } + + private static class CachedStackerForm { + final SimpleForm form; + final long timestamp; + + CachedStackerForm(SimpleForm form) { + this.form = form; + this.timestamp = System.currentTimeMillis(); + } + + boolean isExpired() { + return System.currentTimeMillis() - timestamp > CACHE_EXPIRY_TIME_MS; + } + } +} diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerHandler.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerHandler.java index 70de33f7..44d9f4b8 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerHandler.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/stacker/SpawnerStackerHandler.java @@ -274,7 +274,7 @@ private void processStackModification(Player player, SpawnerData spawner, int ch } } - private void handleStackDecrease(Player player, SpawnerData spawner, int removeAmount) { + public void handleStackDecrease(Player player, SpawnerData spawner, int removeAmount) { // Check if the spawner block still exists to prevent ghost spawner duplication if (plugin.getSpawnerManager().isGhostSpawner(spawner)) { messageService.sendMessage(player, "spawner_invalid"); @@ -317,7 +317,7 @@ private void handleStackDecrease(Player player, SpawnerData spawner, int removeA player.playSound(player.getLocation(), STACK_SOUND, SOUND_VOLUME, SOUND_PITCH); } - private void handleStackIncrease(Player player, SpawnerData spawner, int changeAmount) { + public void handleStackIncrease(Player player, SpawnerData spawner, int changeAmount) { int currentSize = spawner.getStackSize(); int maxStackSize = spawner.getMaxStackSize(); diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/synchronization/SpawnerGuiViewManager.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/synchronization/SpawnerGuiViewManager.java index 3697beb1..34a3722a 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/synchronization/SpawnerGuiViewManager.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/synchronization/SpawnerGuiViewManager.java @@ -861,6 +861,9 @@ public void updateSpawnerMenuViewers(SpawnerData spawner) { if (plugin.getSpawnerMenuUI() != null) { plugin.getSpawnerMenuUI().invalidateSpawnerCache(spawner.getSpawnerId()); } + if (plugin.getSpawnerMenuFormUI() != null) { + plugin.getSpawnerMenuFormUI().invalidateSpawnerCache(spawner.getSpawnerId()); + } int viewerCount = viewers.size(); if (viewerCount > 10) { diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java index 11147ffd..0eb02935 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java @@ -150,6 +150,9 @@ public void recalculateAfterConfigReload() { if (plugin.getSpawnerMenuUI() != null) { plugin.getSpawnerMenuUI().invalidateSpawnerCache(this.spawnerId); } + if (plugin.getSpawnerMenuFormUI() != null) { + plugin.getSpawnerMenuFormUI().invalidateSpawnerCache(this.spawnerId); + } } private void calculateStackBasedValues() { @@ -219,6 +222,9 @@ private void updateStackSize(int newStackSize) { if (plugin.getSpawnerMenuUI() != null) { plugin.getSpawnerMenuUI().invalidateSpawnerCache(this.spawnerId); } + if (plugin.getSpawnerMenuFormUI() != null) { + plugin.getSpawnerMenuFormUI().invalidateSpawnerCache(this.spawnerId); + } } private void recreateVirtualInventory() { @@ -256,6 +262,9 @@ public void setSpawnerExp(int exp) { if (plugin.getSpawnerMenuUI() != null) { plugin.getSpawnerMenuUI().invalidateSpawnerCache(this.spawnerId); } + if (plugin.getSpawnerMenuFormUI() != null) { + plugin.getSpawnerMenuFormUI().invalidateSpawnerCache(this.spawnerId); + } } public void setSpawnerExpData(int exp) { diff --git a/core/src/main/resources/language/DonutSMP/gui.yml b/core/src/main/resources/language/DonutSMP/gui.yml index e3e2caff..106dc50d 100644 --- a/core/src/main/resources/language/DonutSMP/gui.yml +++ b/core/src/main/resources/language/DonutSMP/gui.yml @@ -337,4 +337,41 @@ bedrock: exp: Collect EXP stacker: Manage Stack sell_and_exp: Sell Items & Collect EXP - sell: Sell Items \ No newline at end of file + sell: Sell Items + + # Spawner Info (displayed below buttons in Bedrock UI) + # Available placeholders: see en_US/gui.yml for complete list + # IMPORTANT: Only use Bedrock-compatible color codes (§0-§9, §a-§f, §g) + spawner_info: + - '' + - '§9◈ §fInformation:' + - ' §7• Stack: §9%stack_size%' + - ' §7• Range: §9%range% §7blocks' + - ' §7• Mobs: §9%min_mobs% §7- §9%max_mobs%' + - ' §7• Delay: §9%delay% §7s' + - '' + - '§9◈ §fStorage:' + - ' §7• Items: §9%current_items% §7/ §9%max_items%' + - ' §7• Storage: §9%formatted_storage%% §7full' + - '' + - '§9◈ §fExperience:' + - ' §7• Current: §a%current_exp% §7/ §a%max_exp% §axp' + - ' §7• Stored: §a%formatted_exp%% §7xp' + - '' + - '§a◈ §fSellable Value: §a+$%total_sell_price%' + + # Spawner Stacker GUI (Bedrock Form) + stacker_gui: + title: '§8Spawner Stacker' + + button_decrease: '§c- %amount% Spawner%plural%' + button_increase: '§a+ %amount% Spawner%plural%' + button_back: '§7← Back to Main Menu' + + info_text: + - '§9◈ §fStack Information:' + - ' §7• Current: §9%stack_size%' + - ' §7• Maximum: §9%max_stack_size%' + - ' §7• Entity: §9%entity%' + - '' + - '§7(Tap to refresh)' \ No newline at end of file diff --git a/core/src/main/resources/language/de_DE/gui.yml b/core/src/main/resources/language/de_DE/gui.yml index 2e285295..26d9eb1f 100644 --- a/core/src/main/resources/language/de_DE/gui.yml +++ b/core/src/main/resources/language/de_DE/gui.yml @@ -419,4 +419,41 @@ bedrock: exp: EXP einsammeln stacker: Stack verwalten sell_and_exp: Verkaufen & EXP einsammeln - sell: Verkaufen \ No newline at end of file + sell: Verkaufen + + # Spawner-Informationen (wird unter den Buttons in der Bedrock-UI angezeigt) + # Verfügbare Platzhalter: siehe en_US/gui.yml für vollständige Liste + # WICHTIG: Nur Bedrock-kompatible Farbcodes verwenden (§0-§9, §a-§f, §g) + spawner_info: + - '' + - '§9◈ §fInformationen:' + - ' §7• Stack: §9%stack_size%' + - ' §7• Reichweite: §9%range% §7Blöcke' + - ' §7• Mobs: §9%min_mobs% §7- §9%max_mobs%' + - ' §7• Verzögerung: §9%delay% §7s' + - '' + - '§9◈ §fLager:' + - ' §7• Items: §9%current_items% §7/ §9%max_items%' + - ' §7• Lager: §9%formatted_storage%% §7voll' + - '' + - '§9◈ §fErfahrung:' + - ' §7• Aktuell: §a%current_exp% §7/ §a%max_exp% §aXP' + - ' §7• Gespeichert: §a%formatted_exp%% §7XP' + - '' + - '§a◈ §fVerkaufswert: §a+$%total_sell_price%' + + # Spawner Stacker GUI (Bedrock Formular) + stacker_gui: + title: '§8Spawner Stacker' + + button_decrease: '§c- %amount% Spawner%plural%' + button_increase: '§a+ %amount% Spawner%plural%' + button_back: '§7← Zurück zum Hauptmenü' + + info_text: + - '§9◈ §fStack-Information:' + - ' §7• Aktuell: §9%stack_size%' + - ' §7• Maximum: §9%max_stack_size%' + - ' §7• Entität: §9%entity%' + - '' + - '§7(Tippen zum Aktualisieren)' \ No newline at end of file diff --git a/core/src/main/resources/language/en_US/gui.yml b/core/src/main/resources/language/en_US/gui.yml index 95ef8617..1799e63f 100644 --- a/core/src/main/resources/language/en_US/gui.yml +++ b/core/src/main/resources/language/en_US/gui.yml @@ -419,4 +419,59 @@ bedrock: exp: Collect EXP stacker: Manage Stack sell_and_exp: Sell Items & Collect EXP - sell: Sell Items \ No newline at end of file + sell: Sell Items + + # Spawner Info Configuration (displayed below buttons in Bedrock UI) + # Available placeholders (same as spawner_info_item): + # Entity: %entity%, %ᴇɴᴛɪᴛʏ%, %entity_type% + # Stack: %stack_size% + # Settings: %range%, %delay%, %delay_raw%, %min_mobs%, %max_mobs% + # Storage: %current_items%, %max_items%, %formatted_storage% + # Experience: %current_exp%, %max_exp%, %raw_current_exp%, %raw_max_exp%, %formatted_exp% + # Sell Price: %total_sell_price% + # + # IMPORTANT: Only use Bedrock-compatible color codes: + # §0=Black, §1=Dark Blue, §2=Dark Green, §3=Dark Aqua, §4=Dark Red, §5=Dark Purple + # §6=Gold, §7=Gray, §8=Dark Gray, §9=Blue, §a=Green, §b=Aqua, §c=Red + # §d=Light Purple, §e=Yellow, §f=White, §g=Minecoin Gold + spawner_info: + - '' + - '§9◈ §fInformation:' + - ' §7• Stack: §9%stack_size%' + - ' §7• Range: §9%range% §7blocks' + - ' §7• Mobs: §9%min_mobs% §7- §9%max_mobs%' + - ' §7• Delay: §9%delay% §7s' + - '' + - '§9◈ §fStorage:' + - ' §7• Items: §9%current_items% §7/ §9%max_items%' + - ' §7• Storage: §9%formatted_storage%% §7full' + - '' + - '§9◈ §fExperience:' + - ' §7• Current: §a%current_exp% §7/ §a%max_exp% §axp' + - ' §7• Stored: §a%formatted_exp%% §7xp' + - '' + - '§a◈ §fSellable Value: §a+$%total_sell_price%' + + # Spawner Stacker GUI (Bedrock Form) + stacker_gui: + title: '§8Spawner Stacker' + + # Button text for decreasing stack + # Placeholders: %amount%, %plural%, %stack_size%, %max_stack_size%, %entity%, %ᴇɴᴛɪᴛʏ% + button_decrease: '§c- %amount% Spawner%plural%' + + # Button text for increasing stack + button_increase: '§a+ %amount% Spawner%plural%' + + # Button text for going back to main menu + button_back: '§7← Back to Main Menu' + + # Info text displayed as a button (clicked to refresh) + # Placeholders: %stack_size%, %max_stack_size%, %entity%, %ᴇɴᴛɪᴛʏ% + info_text: + - '§9◈ §fStack Information:' + - ' §7• Current: §9%stack_size%' + - ' §7• Maximum: §9%max_stack_size%' + - ' §7• Entity: §9%entity%' + - '' + - '§7(Tap to refresh)' \ No newline at end of file diff --git a/core/src/main/resources/language/vi_VN/gui.yml b/core/src/main/resources/language/vi_VN/gui.yml index 97b8c95a..995b03ec 100644 --- a/core/src/main/resources/language/vi_VN/gui.yml +++ b/core/src/main/resources/language/vi_VN/gui.yml @@ -387,3 +387,40 @@ bedrock: stacker: Stack Spawner sell_and_exp: Bán Vật Phẩm & Nhận Kinh Nghiệm sell: Bán Vật Phẩm + + # Thông tin Spawner (hiển thị bên dưới các nút trong giao diện Bedrock) + # Placeholder có sẵn: xem en_US/gui.yml để biết danh sách đầy đủ + # QUAN TRỌNG: Chỉ sử dụng mã màu tương thích với Bedrock (§0-§9, §a-§f, §g) + spawner_info: + - '' + - '§9◈ §fThông tin:' + - ' §7• Stack: §9%stack_size%' + - ' §7• Phạm vi: §9%range% §7khối' + - ' §7• Quái: §9%min_mobs% §7- §9%max_mobs%' + - ' §7• Trễ: §9%delay% §7s' + - '' + - '§9◈ §fKho lưu trữ:' + - ' §7• Vật phẩm: §9%current_items% §7/ §9%max_items%' + - ' §7• Kho: §9%formatted_storage%% §7đầy' + - '' + - '§9◈ §fKinh nghiệm:' + - ' §7• Hiện tại: §a%current_exp% §7/ §a%max_exp% §aXP' + - ' §7• Đã lưu: §a%formatted_exp%% §7XP' + - '' + - '§a◈ §fGiá trị bán: §a+$%total_sell_price%' + + # Spawner Stacker GUI (Bedrock Form) + stacker_gui: + title: '§8Spawner Stacker' + + button_decrease: '§c- %amount% Spawner%plural%' + button_increase: '§a+ %amount% Spawner%plural%' + button_back: '§7← Quay lại Menu Chính' + + info_text: + - '§9◈ §fThông tin Stack:' + - ' §7• Hiện tại: §9%stack_size%' + - ' §7• Tối đa: §9%max_stack_size%' + - ' §7• Entity: §9%entity%' + - '' + - '§7(Chạm để làm mới)'