|
2 | 2 |
|
3 | 3 | import github.nighter.smartspawner.SmartSpawner; |
4 | 4 | import github.nighter.smartspawner.language.LanguageManager; |
| 5 | +import github.nighter.smartspawner.nms.VersionInitializer; |
| 6 | +import github.nighter.smartspawner.spawner.config.SpawnerMobHeadTexture; |
5 | 7 | import github.nighter.smartspawner.spawner.gui.layout.GuiButton; |
6 | 8 | import github.nighter.smartspawner.spawner.gui.layout.GuiLayout; |
7 | 9 | import github.nighter.smartspawner.spawner.gui.layout.GuiLayoutConfig; |
| 10 | +import github.nighter.smartspawner.spawner.lootgen.loot.EntityLootConfig; |
| 11 | +import github.nighter.smartspawner.spawner.lootgen.loot.LootItem; |
8 | 12 | import github.nighter.smartspawner.spawner.properties.VirtualInventory; |
9 | 13 | import github.nighter.smartspawner.spawner.properties.SpawnerData; |
10 | 14 | import github.nighter.smartspawner.Scheduler; |
11 | 15 | import github.nighter.smartspawner.Scheduler.Task; |
12 | 16 | import lombok.Getter; |
| 17 | +import net.kyori.adventure.text.Component; |
| 18 | +import org.bukkit.entity.EntityType; |
13 | 19 | import org.bukkit.inventory.Inventory; |
| 20 | +import org.bukkit.inventory.ItemFlag; |
14 | 21 | import org.bukkit.inventory.ItemStack; |
15 | 22 | import org.bukkit.inventory.meta.ItemMeta; |
16 | 23 | import org.bukkit.Bukkit; |
17 | 24 | import org.bukkit.Material; |
18 | 25 |
|
19 | 26 | import java.util.*; |
20 | 27 | import java.util.concurrent.ConcurrentHashMap; |
| 28 | +import java.util.function.Consumer; |
21 | 29 |
|
22 | 30 | public class SpawnerStorageUI { |
23 | 31 | private static final int INVENTORY_SIZE = 54; |
@@ -324,6 +332,12 @@ private void addNavigationButtons(Map<Integer, ItemStack> updates, SpawnerData s |
324 | 332 | continue; |
325 | 333 | } |
326 | 334 |
|
| 335 | + // Handle info_button (spawner mob head with loot info) |
| 336 | + if (button.isInfoButton()) { |
| 337 | + updates.put(button.getSlot(), createStorageSpawnerInfoButton(spawner, button.getMaterial())); |
| 338 | + continue; |
| 339 | + } |
| 340 | + |
327 | 341 | String action = getAnyActionFromButton(button); |
328 | 342 | if (action == null) continue; |
329 | 343 |
|
@@ -521,6 +535,103 @@ private ItemStack createSortButton(SpawnerData spawner, Material material) { |
521 | 535 | return createButton(material, name, lore); |
522 | 536 | } |
523 | 537 |
|
| 538 | + private ItemStack createStorageSpawnerInfoButton(SpawnerData spawner, Material material) { |
| 539 | + Map<VirtualInventory.ItemSignature, Long> storedItems = spawner.getVirtualInventory().getConsolidatedItems(); |
| 540 | + List<Component> lootComponents = buildStorageInfoLootComponents(spawner, storedItems); |
| 541 | + |
| 542 | + Map<String, String> placeholders = new HashMap<>(); |
| 543 | + String entityName; |
| 544 | + if (spawner.isItemSpawner()) { |
| 545 | + entityName = languageManager.getVanillaItemName(spawner.getSpawnedItemMaterial()); |
| 546 | + } else { |
| 547 | + entityName = languageManager.getFormattedMobName(spawner.getEntityType()); |
| 548 | + } |
| 549 | + placeholders.put("entity", entityName); |
| 550 | + placeholders.put("\u1d07\u0274\u1d1b\u026a\u1d1b\u028f", languageManager.getSmallCaps(entityName)); |
| 551 | + placeholders.put("stack_size", String.valueOf(spawner.getStackSize())); |
| 552 | + |
| 553 | + int currentItems = spawner.getVirtualInventory().getUsedSlots(); |
| 554 | + int maxSlots = spawner.getMaxSpawnerLootSlots(); |
| 555 | + double percentStorageDecimal = maxSlots > 0 ? ((double) currentItems / maxSlots) * 100 : 0; |
| 556 | + placeholders.put("percent_storage_decimal", String.format("%.1f", percentStorageDecimal)); |
| 557 | + placeholders.put("percent_storage_rounded", String.valueOf((int) Math.round(percentStorageDecimal))); |
| 558 | + |
| 559 | + long currentExp = spawner.getSpawnerExp(); |
| 560 | + long maxExp = spawner.getMaxStoredExp(); |
| 561 | + double percentExpDecimal = maxExp > 0 ? ((double) currentExp / maxExp) * 100 : 0; |
| 562 | + placeholders.put("percent_exp_decimal", String.format("%.1f", percentExpDecimal)); |
| 563 | + placeholders.put("percent_exp_rounded", String.valueOf((int) Math.round(percentExpDecimal))); |
| 564 | + |
| 565 | + Consumer<ItemMeta> metaModifier = meta -> { |
| 566 | + meta.setDisplayName(languageManager.getGuiItemName("storage_spawner_info_button.name", placeholders)); |
| 567 | + List<Component> lore = languageManager.buildGuiLoreAsComponents( |
| 568 | + "storage_spawner_info_button.lore", placeholders, lootComponents, |
| 569 | + "storage_spawner_info_button.loot_items_empty"); |
| 570 | + if (!lore.isEmpty()) { |
| 571 | + meta.lore(lore); |
| 572 | + } |
| 573 | + meta.addItemFlags(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_UNBREAKABLE); |
| 574 | + }; |
| 575 | + |
| 576 | + ItemStack item; |
| 577 | + if (spawner.isItemSpawner()) { |
| 578 | + item = SpawnerMobHeadTexture.getItemSpawnerHead(spawner.getSpawnedItemMaterial(), metaModifier); |
| 579 | + } else { |
| 580 | + item = SpawnerMobHeadTexture.getCustomHead(spawner.getEntityType(), metaModifier); |
| 581 | + } |
| 582 | + |
| 583 | + if (item.getType() == Material.SPAWNER) { |
| 584 | + VersionInitializer.hideTooltip(item); |
| 585 | + } |
| 586 | + |
| 587 | + return item; |
| 588 | + } |
| 589 | + |
| 590 | + private List<Component> buildStorageInfoLootComponents(SpawnerData spawner, |
| 591 | + Map<VirtualInventory.ItemSignature, Long> storedItems) { |
| 592 | + Map<Material, Long> materialAmountMap = new HashMap<>(); |
| 593 | + for (Map.Entry<VirtualInventory.ItemSignature, Long> entry : storedItems.entrySet()) { |
| 594 | + Material mat = entry.getKey().getTemplateRef().getType(); |
| 595 | + materialAmountMap.merge(mat, entry.getValue(), Long::sum); |
| 596 | + } |
| 597 | + |
| 598 | + EntityLootConfig lootConfig; |
| 599 | + if (spawner.isItemSpawner()) { |
| 600 | + lootConfig = plugin.getItemSpawnerSettingsConfig().getLootConfig(spawner.getSpawnedItemMaterial()); |
| 601 | + } else { |
| 602 | + lootConfig = plugin.getSpawnerSettingsConfig().getLootConfig(spawner.getEntityType()); |
| 603 | + } |
| 604 | + List<LootItem> possibleLootItems = lootConfig != null ? lootConfig.getAllItems() : Collections.emptyList(); |
| 605 | + |
| 606 | + if (possibleLootItems.isEmpty() && storedItems.isEmpty()) { |
| 607 | + return Collections.emptyList(); |
| 608 | + } |
| 609 | + |
| 610 | + List<Component> components = new ArrayList<>(); |
| 611 | + if (!possibleLootItems.isEmpty()) { |
| 612 | + possibleLootItems.sort(Comparator.comparing(item -> item.material().name())); |
| 613 | + for (LootItem lootItem : possibleLootItems) { |
| 614 | + Material mat = lootItem.material(); |
| 615 | + long amount = materialAmountMap.getOrDefault(mat, 0L); |
| 616 | + String formattedAmount = languageManager.formatNumber(amount); |
| 617 | + String chance = String.format("%.1f", lootItem.chance()) + "%"; |
| 618 | + components.add(languageManager.buildTranslatableGuiLootLine( |
| 619 | + "storage_spawner_info_button.loot_items", mat, formattedAmount, chance)); |
| 620 | + } |
| 621 | + } else { |
| 622 | + List<Map.Entry<VirtualInventory.ItemSignature, Long>> sortedItems = new ArrayList<>(storedItems.entrySet()); |
| 623 | + sortedItems.sort(Comparator.comparing(e -> e.getKey().getMaterialName())); |
| 624 | + for (Map.Entry<VirtualInventory.ItemSignature, Long> entry : sortedItems) { |
| 625 | + Material mat = entry.getKey().getTemplateRef().getType(); |
| 626 | + long amount = entry.getValue(); |
| 627 | + String formattedAmount = languageManager.formatNumber(amount); |
| 628 | + components.add(languageManager.buildTranslatableGuiLootLine( |
| 629 | + "storage_spawner_info_button.loot_items", mat, formattedAmount, "")); |
| 630 | + } |
| 631 | + } |
| 632 | + return components; |
| 633 | + } |
| 634 | + |
524 | 635 | private void startCleanupTask() { |
525 | 636 | cleanupTask = Scheduler.runTaskTimer(this::cleanupCaches, 20L * 30, 20L * 30); // Run every 30 seconds |
526 | 637 | } |
|
0 commit comments