From b9b5668fb908aa9973bac8a63d1e218dc0735873 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 1 Aug 2024 14:00:49 -0700 Subject: [PATCH 1/7] Add the ability to hide blocks from the GUI #324 --- .../bentobox/level/config/BlockConfig.java | 31 + .../bentobox/level/panels/DetailsPanel.java | 1282 +++++++++-------- src/main/resources/blockconfig.yml | 4 + 3 files changed, 682 insertions(+), 635 deletions(-) diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java index efcb3ee..b4d0025 100644 --- a/src/main/java/world/bentobox/level/config/BlockConfig.java +++ b/src/main/java/world/bentobox/level/config/BlockConfig.java @@ -2,9 +2,12 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.EnumMap; import java.util.HashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; @@ -26,6 +29,7 @@ public class BlockConfig { private Map blockLimits = new EnumMap<>(Material.class); private Map blockValues = new EnumMap<>(Material.class); private final Map> worldBlockValues = new HashMap<>(); + private final List hiddenBlocks; private Level addon; /** @@ -49,6 +53,16 @@ public BlockConfig(Level addon, YamlConfiguration blockValues, File file) throws if (blockValues.isConfigurationSection("worlds")) { loadWorlds(blockValues); } + // Hidden + hiddenBlocks = blockValues.getStringList("hidden-blocks").stream().map(name -> { + try { + return Material.valueOf(name.toUpperCase(Locale.ENGLISH)); + + } catch (Exception e) { + return null; + } + }).filter(Objects::nonNull).toList(); + // All done blockValues.save(file); } @@ -159,5 +173,22 @@ public Integer getValue(World world, Material md) { return null; } + /** + * Return true if the block should be hidden + * @param m block material + * @return true if hidden + */ + public boolean isHiddenBlock(Material m) { + return hiddenBlocks.contains(m); + } + + /** + * Return true if the block should not be hidden + * @param m block material + * @return false if hidden + */ + public boolean isNotHiddenBlock(Material m) { + return !hiddenBlocks.contains(m); + } } diff --git a/src/main/java/world/bentobox/level/panels/DetailsPanel.java b/src/main/java/world/bentobox/level/panels/DetailsPanel.java index 222174d..90fbf45 100644 --- a/src/main/java/world/bentobox/level/panels/DetailsPanel.java +++ b/src/main/java/world/bentobox/level/panels/DetailsPanel.java @@ -33,84 +33,84 @@ * This class opens GUI that shows generator view for user. */ public class DetailsPanel { - // --------------------------------------------------------------------- - // Section: Internal Constructor - // --------------------------------------------------------------------- - - /** - * This is internal constructor. It is used internally in current class to avoid - * creating objects everywhere. - * - * @param addon Level object - * @param world World where user is operating - * @param user User who opens panel - */ - private DetailsPanel(Level addon, World world, User user) { - this.addon = addon; - this.world = world; - this.user = user; - - this.island = this.addon.getIslands().getIsland(world, user); - - if (this.island != null) { - this.levelsData = this.addon.getManager().getLevelsData(this.island); - } else { - this.levelsData = null; - } - - // By default no-filters are active. - this.activeTab = Tab.VALUE_BLOCKS; - this.activeFilter = Filter.NAME; - this.materialCountList = new ArrayList<>(Material.values().length); - - this.updateFilters(); - } + // --------------------------------------------------------------------- + // Section: Internal Constructor + // --------------------------------------------------------------------- + + /** + * This is internal constructor. It is used internally in current class to avoid + * creating objects everywhere. + * + * @param addon Level object + * @param world World where user is operating + * @param user User who opens panel + */ + private DetailsPanel(Level addon, World world, User user) { + this.addon = addon; + this.world = world; + this.user = user; + + this.island = this.addon.getIslands().getIsland(world, user); + + if (this.island != null) { + this.levelsData = this.addon.getManager().getLevelsData(this.island); + } else { + this.levelsData = null; + } - /** - * This method builds this GUI. - */ - private void build() { - if (this.island == null || this.levelsData == null) { - // Nothing to see. - Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-island")); - return; - } + // By default no-filters are active. + this.activeTab = Tab.VALUE_BLOCKS; + this.activeFilter = Filter.NAME; + this.materialCountList = new ArrayList<>(Material.values().length); + + this.updateFilters(); + } + + /** + * This method builds this GUI. + */ + private void build() { + if (this.island == null || this.levelsData == null) { + // Nothing to see. + Utils.sendMessage(this.user, this.user.getTranslation("general.errors.no-island")); + return; + } - if (this.levelsData.getMdCount().isEmpty() && this.levelsData.getUwCount().isEmpty()) { - // Nothing to see. - Utils.sendMessage(this.user, this.user.getTranslation("level.conversations.no-data")); - return; - } + if (this.levelsData.getMdCount().isEmpty() && this.levelsData.getUwCount().isEmpty()) { + // Nothing to see. + Utils.sendMessage(this.user, this.user.getTranslation("level.conversations.no-data")); + return; + } - // Start building panel. - TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); - panelBuilder.user(this.user); - panelBuilder.world(this.user.getWorld()); + // Start building panel. + TemplatedPanelBuilder panelBuilder = new TemplatedPanelBuilder(); + panelBuilder.user(this.user); + panelBuilder.world(this.user.getWorld()); - panelBuilder.template("detail_panel", new File(this.addon.getDataFolder(), "panels")); + panelBuilder.template("detail_panel", new File(this.addon.getDataFolder(), "panels")); - panelBuilder.parameters("[name]", this.user.getName()); + panelBuilder.parameters("[name]", this.user.getName()); - panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); - panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); - panelBuilder.registerTypeBuilder("BLOCK", this::createMaterialButton); + panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); + panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); + panelBuilder.registerTypeBuilder("BLOCK", this::createMaterialButton); - panelBuilder.registerTypeBuilder("FILTER", this::createFilterButton); + panelBuilder.registerTypeBuilder("FILTER", this::createFilterButton); - // Register tabs - panelBuilder.registerTypeBuilder("TAB", this::createTabButton); + // Register tabs + panelBuilder.registerTypeBuilder("TAB", this::createTabButton); - // Register unknown type builder. - panelBuilder.build(); - } + // Register unknown type builder. + panelBuilder.build(); + } - /** - * This method updates filter of elements based on tabs. - */ - private void updateFilters() { - this.materialCountList.clear(); + /** + * This method updates filter of elements based on tabs. + */ + private void updateFilters() { + this.materialCountList.clear(); - switch (this.activeTab) { + switch (this.activeTab) { case VALUE_BLOCKS -> { Map materialCountMap = new EnumMap<>(Material.class); @@ -126,6 +126,9 @@ private void updateFilters() { return value == null || value == 0; }); + // Remove any hidden blocks + materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock); + materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).forEachOrdered(entry -> { if (entry.getValue() > 0) { this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())); @@ -133,581 +136,590 @@ private void updateFilters() { }); } - case ALL_BLOCKS -> { - Map materialCountMap = new EnumMap<>(Material.class); - - materialCountMap.putAll(this.levelsData.getMdCount()); - - // Add underwater blocks. - this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material, - materialCountMap.computeIfAbsent(material, key -> 0) + count)); - - materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())) - .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); - } - case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream().sorted((Map.Entry.comparingByKey())) - .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); - - case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream().sorted((Map.Entry.comparingByKey())) - .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); - - case SPAWNER -> { - int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0); - int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0); - - // TODO: spawners need some touch... - this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater)); - } - } - - Comparator> sorter; - - switch (this.activeFilter) { - case COUNT -> { - sorter = (o1, o2) -> { - if (o1.getValue().equals(o2.getValue())) { - String o1Name = Utils.prettifyObject(o1.getKey(), this.user); - String o2Name = Utils.prettifyObject(o2.getKey(), this.user); - - return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); - } else { - return Integer.compare(o2.getValue(), o1.getValue()); - } - }; - } - case VALUE -> { - sorter = (o1, o2) -> { - int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o1.getKey(), 0); - int o1Count = blockLimit > 0 ? Math.min(o1.getValue(), blockLimit) : o1.getValue(); - - blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o2.getKey(), 0); - int o2Count = blockLimit > 0 ? Math.min(o2.getValue(), blockLimit) : o2.getValue(); - - long o1Value = (long) o1Count - * this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.getKey(), 0); - long o2Value = (long) o2Count - * this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.getKey(), 0); - - if (o1Value == o2Value) { - String o1Name = Utils.prettifyObject(o1.getKey(), this.user); - String o2Name = Utils.prettifyObject(o2.getKey(), this.user); - - return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); - } else { - return Long.compare(o2Value, o1Value); - } - }; - } - default -> { - sorter = (o1, o2) -> { - String o1Name = Utils.prettifyObject(o1.getKey(), this.user); - String o2Name = Utils.prettifyObject(o2.getKey(), this.user); - - return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); - }; - } - } - - this.materialCountList.sort(sorter); - - this.pageIndex = 0; - } - - // --------------------------------------------------------------------- - // Section: Tab Button Type - // --------------------------------------------------------------------- - - /** - * Create tab button panel item. - * - * @param template the template - * @param slot the slot - * @return the panel item - */ - private PanelItem createTabButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - PanelItemBuilder builder = new PanelItemBuilder(); - - if (template.icon() != null) { - // Set icon - builder.icon(template.icon().clone()); - } - - if (template.title() != null) { - // Set title - builder.name(this.user.getTranslation(this.world, template.title())); - } - - if (template.description() != null) { - // Set description - builder.description(this.user.getTranslation(this.world, template.description())); - } + case ALL_BLOCKS -> { + Map materialCountMap = new EnumMap<>(Material.class); + + materialCountMap.putAll(this.levelsData.getMdCount()); + + // Add underwater blocks. + this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material, + materialCountMap.computeIfAbsent(material, key -> 0) + count)); + + // Remove any hidden blocks + materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock); + + materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())) + .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); + } + case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream() + .filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey())) + .sorted((Map.Entry.comparingByKey())) + .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); + + case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream() + .filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey())) + .sorted((Map.Entry.comparingByKey())) + .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); + + case SPAWNER -> { + if (this.addon.getBlockConfig().isNotHiddenBlock(Material.SPAWNER)) { + int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0); + int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0); + + // TODO: spawners need some touch... + this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater)); + } + } + } + + Comparator> sorter; + + switch (this.activeFilter) { + case COUNT -> { + sorter = (o1, o2) -> { + if (o1.getValue().equals(o2.getValue())) { + String o1Name = Utils.prettifyObject(o1.getKey(), this.user); + String o2Name = Utils.prettifyObject(o2.getKey(), this.user); + + return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); + } else { + return Integer.compare(o2.getValue(), o1.getValue()); + } + }; + } + case VALUE -> { + sorter = (o1, o2) -> { + int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o1.getKey(), 0); + int o1Count = blockLimit > 0 ? Math.min(o1.getValue(), blockLimit) : o1.getValue(); + + blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o2.getKey(), 0); + int o2Count = blockLimit > 0 ? Math.min(o2.getValue(), blockLimit) : o2.getValue(); + + long o1Value = (long) o1Count + * this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.getKey(), 0); + long o2Value = (long) o2Count + * this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.getKey(), 0); + + if (o1Value == o2Value) { + String o1Name = Utils.prettifyObject(o1.getKey(), this.user); + String o2Name = Utils.prettifyObject(o2.getKey(), this.user); + + return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); + } else { + return Long.compare(o2Value, o1Value); + } + }; + } + default -> { + sorter = (o1, o2) -> { + String o1Name = Utils.prettifyObject(o1.getKey(), this.user); + String o2Name = Utils.prettifyObject(o2.getKey(), this.user); + + return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); + }; + } + } + + this.materialCountList.sort(sorter); + + this.pageIndex = 0; + } + + // --------------------------------------------------------------------- + // Section: Tab Button Type + // --------------------------------------------------------------------- + + /** + * Create tab button panel item. + * + * @param template the template + * @param slot the slot + * @return the panel item + */ + private PanelItem createTabButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) { + // Set icon + builder.icon(template.icon().clone()); + } + + if (template.title() != null) { + // Set title + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) { + // Set description + builder.description(this.user.getTranslation(this.world, template.description())); + } Tab tab = Enums.getIfPresent(Tab.class, String.valueOf(template.dataMap().get("tab"))).or(Tab.VALUE_BLOCKS); - // Get only possible actions, by removing all inactive ones. - List activeActions = new ArrayList<>(template.actions()); - - activeActions.removeIf(action -> "VIEW".equalsIgnoreCase(action.actionType()) && this.activeTab == tab); - - // Add Click handler - builder.clickHandler((panel, user, clickType, i) -> { - for (ItemTemplateRecord.ActionRecords action : activeActions) { - if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) - && "VIEW".equalsIgnoreCase(action.actionType())) { - this.activeTab = tab; - - // Update filters. - this.updateFilters(); - this.build(); - } - } - - return true; - }); - - // Collect tooltips. - List tooltips = activeActions.stream().filter(action -> action.tooltip() != null) - .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) - .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); - - // Add tooltips. - if (!tooltips.isEmpty()) { - // Empty line and tooltips. - builder.description(""); - builder.description(tooltips); - } - - builder.glow(this.activeTab == tab); - - return builder.build(); - } - - /** - * Create next button panel item. - * - * @param template the template - * @param slot the slot - * @return the panel item - */ - private PanelItem createFilterButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - PanelItemBuilder builder = new PanelItemBuilder(); - - if (template.icon() != null) { - // Set icon - builder.icon(template.icon().clone()); - } - - Filter filter; - - if (slot.amountMap().getOrDefault("FILTER", 0) > 1) { - filter = Enums.getIfPresent(Filter.class, String.valueOf(template.dataMap().get("filter"))).or(Filter.NAME); - } else { - filter = this.activeFilter; - } - - final String reference = "level.gui.buttons.filters."; - - if (template.title() != null) { - // Set title - builder.name(this.user.getTranslation(this.world, - template.title().replace("[filter]", filter.name().toLowerCase()))); - } else { - builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".name")); - } - - if (template.description() != null) { - // Set description - builder.description(this.user.getTranslation(this.world, - template.description().replace("[filter]", filter.name().toLowerCase()))); - } else { - builder.name( - this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".description")); - } - - // Get only possible actions, by removing all inactive ones. - List activeActions = new ArrayList<>(template.actions()); - - // Add Click handler - builder.clickHandler((panel, user, clickType, i) -> { - for (ItemTemplateRecord.ActionRecords action : activeActions) { - if (clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) { - if ("UP".equalsIgnoreCase(action.actionType())) { - this.activeFilter = Utils.getNextValue(Filter.values(), filter); - - // Update filters. - this.updateFilters(); - this.build(); - } else if ("DOWN".equalsIgnoreCase(action.actionType())) { - this.activeFilter = Utils.getPreviousValue(Filter.values(), filter); - - // Update filters. - this.updateFilters(); - this.build(); - } else if ("SELECT".equalsIgnoreCase(action.actionType())) { - this.activeFilter = filter; - - // Update filters. - this.updateFilters(); - this.build(); - } - } - } - - return true; - }); - - // Collect tooltips. - List tooltips = activeActions.stream().filter(action -> action.tooltip() != null) - .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) - .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); - - // Add tooltips. - if (!tooltips.isEmpty()) { - // Empty line and tooltips. - builder.description(""); - builder.description(tooltips); - } - - builder.glow(this.activeFilter == filter); - - return builder.build(); - } - - // --------------------------------------------------------------------- - // Section: Create common buttons - // --------------------------------------------------------------------- - - /** - * Create next button panel item. - * - * @param template the template - * @param slot the slot - * @return the panel item - */ - private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - long size = this.materialCountList.size(); - - if (size <= slot.amountMap().getOrDefault("BLOCK", 1) - || 1.0 * size / slot.amountMap().getOrDefault("BLOCK", 1) <= this.pageIndex + 1) { - // There are no next elements - return null; - } - - int nextPageIndex = this.pageIndex + 2; - - PanelItemBuilder builder = new PanelItemBuilder(); - - if (template.icon() != null) { - ItemStack clone = template.icon().clone(); - - if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false))) { - clone.setAmount(nextPageIndex); - } - - builder.icon(clone); - } - - if (template.title() != null) { - builder.name(this.user.getTranslation(this.world, template.title())); - } - - if (template.description() != null) { - builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER, - String.valueOf(nextPageIndex))); - } - - // Add ClickHandler - builder.clickHandler((panel, user, clickType, i) -> { - for (ItemTemplateRecord.ActionRecords action : template.actions()) { - if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) - && "NEXT".equalsIgnoreCase(action.actionType())) { - this.pageIndex++; - this.build(); - } - } - - // Always return true. - return true; - }); - - // Collect tooltips. - List tooltips = template.actions().stream().filter(action -> action.tooltip() != null) - .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) - .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); - - // Add tooltips. - if (!tooltips.isEmpty()) { - // Empty line and tooltips. - builder.description(""); - builder.description(tooltips); - } - - return builder.build(); - } - - /** - * Create previous button panel item. - * - * @param template the template - * @param slot the slot - * @return the panel item - */ - private PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - if (this.pageIndex == 0) { - // There are no next elements - return null; - } - - int previousPageIndex = this.pageIndex; - - PanelItemBuilder builder = new PanelItemBuilder(); - - if (template.icon() != null) { - ItemStack clone = template.icon().clone(); - - if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false))) { - clone.setAmount(previousPageIndex); - } - - builder.icon(clone); - } - - if (template.title() != null) { - builder.name(this.user.getTranslation(this.world, template.title())); - } - - if (template.description() != null) { - builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER, - String.valueOf(previousPageIndex))); - } - - // Add ClickHandler - builder.clickHandler((panel, user, clickType, i) -> { - for (ItemTemplateRecord.ActionRecords action : template.actions()) { - if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) - && "PREVIOUS".equalsIgnoreCase(action.actionType())) { - this.pageIndex--; - this.build(); - } - } - - // Always return true. - return true; - }); - - // Collect tooltips. - List tooltips = template.actions().stream().filter(action -> action.tooltip() != null) - .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) - .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); - - // Add tooltips. - if (!tooltips.isEmpty()) { - // Empty line and tooltips. - builder.description(""); - builder.description(tooltips); - } - - return builder.build(); - } - - // --------------------------------------------------------------------- - // Section: Create Material Button - // --------------------------------------------------------------------- - - /** - * Create material button panel item. - * - * @param template the template - * @param slot the slot - * @return the panel item - */ - private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - if (this.materialCountList.isEmpty()) { - // Does not contain any generators. - return null; - } - - int index = this.pageIndex * slot.amountMap().getOrDefault("BLOCK", 1) + slot.slot(); - - if (index >= this.materialCountList.size()) { - // Out of index. - return null; - } - - return this.createMaterialButton(template, this.materialCountList.get(index)); - } - - /** - * This method creates button for material. - * - * @param template the template of the button - * @param materialCount materialCount which button must be created. - * @return PanelItem for generator tier. - */ - private PanelItem createMaterialButton(ItemTemplateRecord template, Pair materialCount) { - PanelItemBuilder builder = new PanelItemBuilder(); - - if (template.icon() != null) { - builder.icon(template.icon().clone()); - } else { - builder.icon(PanelUtils.getMaterialItem(materialCount.getKey())); - } - - if (materialCount.getValue() < 64) { - builder.amount(materialCount.getValue()); - } - - if (template.title() != null) { - builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, - String.valueOf(materialCount.getValue()), "[material]", - Utils.prettifyObject(materialCount.getKey(), this.user))); - } - - String description = Utils.prettifyDescription(materialCount.getKey(), this.user); - - final String reference = "level.gui.buttons.material."; - String blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", materialCount.getKey().name()); - - int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(materialCount.getKey(), 0); - String value = blockValue > 0 - ? this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER, - String.valueOf(blockValue)) - : ""; - - int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(materialCount.getKey(), 0); - String limit = blockLimit > 0 - ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER, - String.valueOf(blockLimit)) - : ""; - - String count = this.user.getTranslationOrNothing(reference + "count", TextVariables.NUMBER, - String.valueOf(materialCount.getValue())); - - long calculatedValue = (long) Math.min(blockLimit > 0 ? blockLimit : Integer.MAX_VALUE, - materialCount.getValue()) * blockValue; - String valueText = calculatedValue > 0 ? this.user.getTranslationOrNothing(reference + "calculated", - TextVariables.NUMBER, String.valueOf(calculatedValue)) : ""; - - if (template.description() != null) { - builder.description(this.user - .getTranslation(this.world, template.description(), "[description]", description, "[id]", blockId, - "[value]", value, "[calculated]", valueText, "[limit]", limit, "[count]", count) - .replaceAll("(?m)^[ \\t]*\\r?\\n", "").replaceAll("(? activeActions = new ArrayList<>(template.actions()); + + activeActions.removeIf(action -> "VIEW".equalsIgnoreCase(action.actionType()) && this.activeTab == tab); + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : activeActions) { + if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) + && "VIEW".equalsIgnoreCase(action.actionType())) { + this.activeTab = tab; + + // Update filters. + this.updateFilters(); + this.build(); + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = activeActions.stream().filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + builder.glow(this.activeTab == tab); + + return builder.build(); + } + + /** + * Create next button panel item. + * + * @param template the template + * @param slot the slot + * @return the panel item + */ + private PanelItem createFilterButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) { + // Set icon + builder.icon(template.icon().clone()); + } + + Filter filter; + + if (slot.amountMap().getOrDefault("FILTER", 0) > 1) { + filter = Enums.getIfPresent(Filter.class, String.valueOf(template.dataMap().get("filter"))).or(Filter.NAME); + } else { + filter = this.activeFilter; + } + + final String reference = "level.gui.buttons.filters."; + + if (template.title() != null) { + // Set title + builder.name(this.user.getTranslation(this.world, + template.title().replace("[filter]", filter.name().toLowerCase()))); + } else { + builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".name")); + } + + if (template.description() != null) { + // Set description + builder.description(this.user.getTranslation(this.world, + template.description().replace("[filter]", filter.name().toLowerCase()))); + } else { + builder.name( + this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".description")); + } + + // Get only possible actions, by removing all inactive ones. + List activeActions = new ArrayList<>(template.actions()); + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : activeActions) { + if (clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) { + if ("UP".equalsIgnoreCase(action.actionType())) { + this.activeFilter = Utils.getNextValue(Filter.values(), filter); + + // Update filters. + this.updateFilters(); + this.build(); + } else if ("DOWN".equalsIgnoreCase(action.actionType())) { + this.activeFilter = Utils.getPreviousValue(Filter.values(), filter); + + // Update filters. + this.updateFilters(); + this.build(); + } else if ("SELECT".equalsIgnoreCase(action.actionType())) { + this.activeFilter = filter; + + // Update filters. + this.updateFilters(); + this.build(); + } + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = activeActions.stream().filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + builder.glow(this.activeFilter == filter); + + return builder.build(); + } + + // --------------------------------------------------------------------- + // Section: Create common buttons + // --------------------------------------------------------------------- + + /** + * Create next button panel item. + * + * @param template the template + * @param slot the slot + * @return the panel item + */ + private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { + long size = this.materialCountList.size(); + + if (size <= slot.amountMap().getOrDefault("BLOCK", 1) + || 1.0 * size / slot.amountMap().getOrDefault("BLOCK", 1) <= this.pageIndex + 1) { + // There are no next elements + return null; + } + + int nextPageIndex = this.pageIndex + 2; + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) { + ItemStack clone = template.icon().clone(); + + if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false))) { + clone.setAmount(nextPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) { + builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER, + String.valueOf(nextPageIndex))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : template.actions()) { + if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) + && "NEXT".equalsIgnoreCase(action.actionType())) { + this.pageIndex++; + this.build(); + } + } + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream().filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + /** + * Create previous button panel item. + * + * @param template the template + * @param slot the slot + * @return the panel item + */ + private PanelItem createPreviousButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { + if (this.pageIndex == 0) { + // There are no next elements + return null; + } + + int previousPageIndex = this.pageIndex; + + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) { + ItemStack clone = template.icon().clone(); + + if (Boolean.TRUE.equals(template.dataMap().getOrDefault("indexing", false))) { + clone.setAmount(previousPageIndex); + } + + builder.icon(clone); + } + + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title())); + } + + if (template.description() != null) { + builder.description(this.user.getTranslation(this.world, template.description(), TextVariables.NUMBER, + String.valueOf(previousPageIndex))); + } + + // Add ClickHandler + builder.clickHandler((panel, user, clickType, i) -> { + for (ItemTemplateRecord.ActionRecords action : template.actions()) { + if ((clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) + && "PREVIOUS".equalsIgnoreCase(action.actionType())) { + this.pageIndex--; + this.build(); + } + } + + // Always return true. + return true; + }); + + // Collect tooltips. + List tooltips = template.actions().stream().filter(action -> action.tooltip() != null) + .map(action -> this.user.getTranslation(this.world, action.tooltip())).filter(text -> !text.isBlank()) + .collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + return builder.build(); + } + + // --------------------------------------------------------------------- + // Section: Create Material Button + // --------------------------------------------------------------------- + + /** + * Create material button panel item. + * + * @param template the template + * @param slot the slot + * @return the panel item + */ + private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { + if (this.materialCountList.isEmpty()) { + // Does not contain any generators. + return null; + } + + int index = this.pageIndex * slot.amountMap().getOrDefault("BLOCK", 1) + slot.slot(); + + if (index >= this.materialCountList.size()) { + // Out of index. + return null; + } + + return this.createMaterialButton(template, this.materialCountList.get(index)); + } + + /** + * This method creates button for material. + * + * @param template the template of the button + * @param materialCount materialCount which button must be created. + * @return PanelItem for generator tier. + */ + private PanelItem createMaterialButton(ItemTemplateRecord template, Pair materialCount) { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) { + builder.icon(template.icon().clone()); + } else { + builder.icon(PanelUtils.getMaterialItem(materialCount.getKey())); + } + + if (materialCount.getValue() < 64) { + builder.amount(materialCount.getValue()); + } + + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, + String.valueOf(materialCount.getValue()), "[material]", + Utils.prettifyObject(materialCount.getKey(), this.user))); + } + + String description = Utils.prettifyDescription(materialCount.getKey(), this.user); + + final String reference = "level.gui.buttons.material."; + String blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", materialCount.getKey().name()); + + int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(materialCount.getKey(), 0); + String value = blockValue > 0 + ? this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER, + String.valueOf(blockValue)) + : ""; + + int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(materialCount.getKey(), 0); + String limit = blockLimit > 0 + ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER, + String.valueOf(blockLimit)) + : ""; + + String count = this.user.getTranslationOrNothing(reference + "count", TextVariables.NUMBER, + String.valueOf(materialCount.getValue())); + + long calculatedValue = (long) Math.min(blockLimit > 0 ? blockLimit : Integer.MAX_VALUE, + materialCount.getValue()) * blockValue; + String valueText = calculatedValue > 0 ? this.user.getTranslationOrNothing(reference + "calculated", + TextVariables.NUMBER, String.valueOf(calculatedValue)) : ""; + + if (template.description() != null) { + builder.description(this.user + .getTranslation(this.world, template.description(), "[description]", description, "[id]", blockId, + "[value]", value, "[calculated]", valueText, "[limit]", limit, "[count]", count) + .replaceAll("(?m)^[ \\t]*\\r?\\n", "").replaceAll("(?> materialCountList; - - /** - * This variable holds current pageIndex for multi-page generator choosing. - */ - private int pageIndex; - - /** - * This variable stores which tab currently is active. - */ - private Tab activeTab; - - /** - * This variable stores active filter for items. - */ - private Filter activeFilter; + ABOVE_SEA_LEVEL, + /** + * Underwater Tab. + */ + UNDERWATER, + /** + * Spawner Tab. + */ + SPAWNER + } + + /** + * Sorting order of blocks. + */ + private enum Filter { + /** + * By name + */ + NAME, + /** + * By value + */ + VALUE, + /** + * By number + */ + COUNT + } + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This variable holds targeted island. + */ + private final Island island; + + /** + * This variable holds targeted island level data. + */ + private final IslandLevels levelsData; + + /** + * This variable allows to access addon object. + */ + private final Level addon; + + /** + * This variable holds user who opens panel. Without it panel cannot be opened. + */ + private final User user; + + /** + * This variable holds a world to which gui referee. + */ + private final World world; + + /** + * This variable stores the list of elements to display. + */ + private final List> materialCountList; + + /** + * This variable holds current pageIndex for multi-page generator choosing. + */ + private int pageIndex; + + /** + * This variable stores which tab currently is active. + */ + private Tab activeTab; + + /** + * This variable stores active filter for items. + */ + private Filter activeFilter; } diff --git a/src/main/resources/blockconfig.yml b/src/main/resources/blockconfig.yml index 3af622c..e9fe8ba 100644 --- a/src/main/resources/blockconfig.yml +++ b/src/main/resources/blockconfig.yml @@ -10,6 +10,10 @@ limits: COBBLESTONE: 10000 NETHERRACK: 1000 +# These blocks will never be shown in the GUI even if they have value +hidden-blocks: + - BEDROCK + - AIR blocks: ACACIA_BUTTON: 1 ACACIA_DOOR: 2 From 95a119c7cd55463e4ea2c3bcfc3729a0a653cc50 Mon Sep 17 00:00:00 2001 From: tastybento Date: Tue, 6 Aug 2024 07:16:50 -0700 Subject: [PATCH 2/7] Use hidden blocks in values panel #324 --- src/main/java/world/bentobox/level/panels/ValuePanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/world/bentobox/level/panels/ValuePanel.java b/src/main/java/world/bentobox/level/panels/ValuePanel.java index e97ca54..5af78e4 100644 --- a/src/main/java/world/bentobox/level/panels/ValuePanel.java +++ b/src/main/java/world/bentobox/level/panels/ValuePanel.java @@ -59,6 +59,7 @@ private ValuePanel(Level addon, this.materialRecordList = Arrays.stream(Material.values()). filter(Material::isBlock). filter(m -> !m.name().startsWith("LEGACY_")). + filter(this.addon.getBlockConfig()::isNotHiddenBlock). map(material -> { Integer value = this.addon.getBlockConfig().getValue(this.world, material); From 2d0382a14b31c3f83e2e8b81e2154393cf089e5d Mon Sep 17 00:00:00 2001 From: tastybento Date: Wed, 21 Aug 2024 07:39:06 -0700 Subject: [PATCH 3/7] Updated comments on tipped arrow --- src/main/resources/panels/detail_panel.yml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/main/resources/panels/detail_panel.yml b/src/main/resources/panels/detail_panel.yml index 5a33da6..d91b228 100644 --- a/src/main/resources/panels/detail_panel.yml +++ b/src/main/resources/panels/detail_panel.yml @@ -132,17 +132,10 @@ detail_panel: 8: material_button 3: 1: - # In this case, the icon is defined as a TIPPED_ARROW with and enchantment applied. The format for the enchantment is - # define in {@link world.bentobox.bentobox.util.ItemParser} and available for POTIONS or TIPPED_ARROWs. - # Format TIPPED_ARROW:NAME::::QTY - # LEVEL, EXTENDED, SPLASH, LINGER are optional. - # LEVEL is a number, 1 or 2 - # LINGER is for V1.9 servers and later - # Examples: - # TIPPED_ARROW:STRENGTH:1:EXTENDED:SPLASH:1 - # TIPPED_ARROW:INSTANT_DAMAGE:2::LINGER:2 - # TIPPED_ARROW:JUMP:2:NOTEXTENDED:NOSPLASH:1 - # TIPPED_ARROW:WEAKNESS::::1 - any weakness enchantment + # In this case, the icon is defined as a TIPPED_ARROW with a color. + # CustomPotionColor uses the Decimal description of a Color, just as leather armor does. + # All you need to do is take a hex code of a color (like #ff00aa) which represents red, + # green, blue as 2 hex digits each and convert that number into a decimal, using a hex to decimal calculator. icon: tipped_arrow{CustomPotionColor:11546150} title: level.gui.buttons.previous.name description: level.gui.buttons.previous.description From e2a62c17eda3f2ad8242dcbfb010f0b7d661c3d5 Mon Sep 17 00:00:00 2001 From: tastybento Date: Wed, 21 Aug 2024 08:19:11 -0700 Subject: [PATCH 4/7] Fix POM and update API --- pom.xml | 2 +- src/main/resources/addon.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 14c6740..43a91e8 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 2.0.9 1.20.4-R0.1-SNAPSHOT - 2.4.0-SNAPSHOT + 2.5.1-SNAPSHOT 1.12.0 diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index 9edbf7d..0ce35f8 100755 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -2,7 +2,7 @@ name: Level main: world.bentobox.level.Level version: ${version}${build.number} icon: DIAMOND -api-version: 2.4.0 +api-version: 2.5.1 authors: tastybento From 48fbd4b38d2a45e85f027b382776ec76242644f3 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 29 Aug 2024 08:24:38 -0700 Subject: [PATCH 5/7] Version 2.16.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 43a91e8..57014a2 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ -LOCAL - 2.15.0 + 2.16.0 BentoBoxWorld_Level bentobox-world https://sonarcloud.io From a09f68e8af9df9529d6766e26fdde807691dd822 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 29 Aug 2024 08:24:55 -0700 Subject: [PATCH 6/7] Custom units for shorthand levels added to config.yml --- .../world/bentobox/level/LevelsManager.java | 16 ++--- .../bentobox/level/config/ConfigSettings.java | 68 +++++++++++++++++++ src/main/resources/config.yml | 25 +++++-- .../bentobox/level/LevelsManagerTest.java | 5 +- 4 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/main/java/world/bentobox/level/LevelsManager.java b/src/main/java/world/bentobox/level/LevelsManager.java index 7e6163e..39316f6 100644 --- a/src/main/java/world/bentobox/level/LevelsManager.java +++ b/src/main/java/world/bentobox/level/LevelsManager.java @@ -36,16 +36,8 @@ public class LevelsManager { private static final String INTOPTEN = "intopten"; - private static final TreeMap LEVELS; + private static final TreeMap LEVELS = new TreeMap<>(); private static final BigInteger THOUSAND = BigInteger.valueOf(1000); - static { - LEVELS = new TreeMap<>(); - - LEVELS.put(THOUSAND, "k"); - LEVELS.put(THOUSAND.pow(2), "M"); - LEVELS.put(THOUSAND.pow(3), "G"); - LEVELS.put(THOUSAND.pow(4), "T"); - } private final Level addon; // Database handler for level data @@ -67,6 +59,12 @@ public LevelsManager(Level addon) { levelsCache = new HashMap<>(); // Initialize top ten lists topTenLists = new ConcurrentHashMap<>(); + // Units + LEVELS.put(THOUSAND, addon.getSettings().getKilo()); + LEVELS.put(THOUSAND.pow(2), addon.getSettings().getMega()); + LEVELS.put(THOUSAND.pow(3), addon.getSettings().getGiga()); + LEVELS.put(THOUSAND.pow(4), addon.getSettings().getTera()); + } public void migrate() { diff --git a/src/main/java/world/bentobox/level/config/ConfigSettings.java b/src/main/java/world/bentobox/level/config/ConfigSettings.java index c287d2a..e865f47 100644 --- a/src/main/java/world/bentobox/level/config/ConfigSettings.java +++ b/src/main/java/world/bentobox/level/config/ConfigSettings.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.configuration.ConfigComment; @@ -120,6 +121,17 @@ public class ConfigSettings implements ConfigObject { @ConfigComment("Shows large level values rounded down, e.g., 10,345 -> 10k") @ConfigEntry(path = "shorthand") private boolean shorthand = false; + + @ConfigComment("Shorthand units") + @ConfigEntry(path = "units.kilo") + private String kilo = "k"; + @ConfigEntry(path = "units.mega") + private String mega = "M"; + @ConfigEntry(path = "units.giga") + private String giga = "G"; + @ConfigEntry(path = "units.tera") + private String tera = "T"; + @ConfigComment("") @ConfigComment("Include Shulker Box content in chests in level calculations.") @ConfigComment("Will count blocks in Shulker Boxes inside of chests.") @@ -419,4 +431,60 @@ public List getDisabledPluginHooks() { public void setDisabledPluginHooks(List disabledPluginHooks) { this.disabledPluginHooks = disabledPluginHooks; } + + /** + * @return the kilo + */ + public String getKilo() { + return Objects.requireNonNullElse(kilo, "k"); + } + + /** + * @param kilo the kilo to set + */ + public void setKilo(String kilo) { + this.kilo = kilo; + } + + /** + * @return the mega + */ + public String getMega() { + return Objects.requireNonNullElse(mega, "M"); + } + + /** + * @param mega the mega to set + */ + public void setMega(String mega) { + this.mega = mega; + } + + /** + * @return the giga + */ + public String getGiga() { + return Objects.requireNonNullElse(giga, "G"); + } + + /** + * @param giga the giga to set + */ + public void setGiga(String giga) { + this.giga = giga; + } + + /** + * @return the tera + */ + public String getTera() { + return Objects.requireNonNullElse(tera, "T"); + } + + /** + * @param tera the tera to set + */ + public void setTera(String tera) { + this.tera = tera; + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7f2ed95..4b98666 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -4,22 +4,22 @@ # Disabled Game Mode Addons # Level will NOT hook into these game mode addons. disabled-game-modes: [] -# +# # When executing level command from console, should a report be shown? log-report-to-console: true -# +# # Number of concurrent island calculations # If your CPU can handle it, you can run parallel island calcs if there are more than one in the queue concurrent-island-calcs: 1 -# +# # Island level calculation timeout in minutes. # If an island takes longer that this time to calculate, then the calculation will abort. # Generally, calculation should only take a few seconds, so if this ever triggers then something is not right. calculation-timeout: 5 -# +# # Zero island levels on new island or island reset # If true, Level will calculate the starter island's level and remove it from any future level calculations. -# If this is false, the player's starter island blocks will count towards their level. +# If false, the player's starter island blocks will count towards their level. # This will reduce CPU if false. zero-new-island-levels: true # @@ -72,3 +72,18 @@ sumteamdeaths: false # Shorthand island level # Shows large level values rounded down, e.g., 10,345 -> 10k shorthand: false +units: + # Shorthand units + kilo: k + mega: M + giga: G + tera: T +# +# Include Shulker Box content in chests in level calculations. +# Will count blocks in Shulker Boxes inside of chests. +# NOTE: include-chests needs to be enabled for this to work!. +include-shulkers-in-chest: false +# +# Disables hooking with other plugins. +# Example: disabled-plugin-hooks: [UltimateStacker, RoseStacker] +disabled-plugin-hooks: [] \ No newline at end of file diff --git a/src/test/java/world/bentobox/level/LevelsManagerTest.java b/src/test/java/world/bentobox/level/LevelsManagerTest.java index c0be60c..e8b0361 100644 --- a/src/test/java/world/bentobox/level/LevelsManagerTest.java +++ b/src/test/java/world/bentobox/level/LevelsManagerTest.java @@ -95,7 +95,7 @@ public class LevelsManagerTest { private World world; @Mock private Player player; - @Mock + private ConfigSettings settings; @Mock private User user; @@ -176,6 +176,7 @@ public void setUp() throws Exception { when(world.getName()).thenReturn("bskyblock-world"); // Settings + settings = new ConfigSettings(); when(addon.getSettings()).thenReturn(settings); // User @@ -334,7 +335,7 @@ public void testGetLevelsData() { @Test public void testFormatLevel() { assertEquals("123456789", lm.formatLevel(123456789L)); - when(settings.isShorthand()).thenReturn(true); + settings.setShorthand(true); assertEquals("123.5M", lm.formatLevel(123456789L)); assertEquals("1.2k", lm.formatLevel(1234L)); assertEquals("123.5G", lm.formatLevel(123456789352L)); From 09c83fa48efcae778e9a44ee7ff93e7a8e578def Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 16 Sep 2024 19:08:26 -0700 Subject: [PATCH 7/7] Adds a +/- tp the sethandicap admin command #329 --- .../commands/AdminSetInitialLevelCommand.java | 34 +++++++++++++++---- src/main/resources/locales/en-US.yml | 8 +++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/main/java/world/bentobox/level/commands/AdminSetInitialLevelCommand.java b/src/main/java/world/bentobox/level/commands/AdminSetInitialLevelCommand.java index 0c0f89b..05134d8 100644 --- a/src/main/java/world/bentobox/level/commands/AdminSetInitialLevelCommand.java +++ b/src/main/java/world/bentobox/level/commands/AdminSetInitialLevelCommand.java @@ -46,10 +46,20 @@ public Optional> tabComplete(User user, String alias, List @Override public boolean execute(User user, String label, List args) { - String initialLevel = String.valueOf(addon.getManager().getInitialLevel(island)); - long lv = Long.parseLong(args.get(1)); + long initialLevel = addon.getManager().getInitialLevel(island); + long lv = 0; + if (args.get(1).startsWith("+")) { + String change = args.get(1).substring(1); + lv = initialLevel + Long.parseLong(change); + } else if (args.get(1).startsWith("-")) { + String change = args.get(1).substring(1); + lv = initialLevel - Long.parseLong(change); + } else { + lv = Long.parseLong(args.get(1)); + } addon.getManager().setInitialIslandLevel(island, lv); - user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, initialLevel, "[new_number]", String.valueOf(lv)); + user.sendMessage("admin.level.sethandicap.changed", TextVariables.NUMBER, String.valueOf(initialLevel), + "[new_number]", String.valueOf(lv)); return true; } @@ -64,10 +74,20 @@ public boolean canExecute(User user, String label, List args) { user.sendMessage("general.errors.unknown-player", TextVariables.NAME, args.get(0)); return false; } - // Check value - if (!Util.isInteger(args.get(1), true)) { - user.sendMessage("admin.level.sethandicap.invalid-level"); - return false; + // Check if this is a add or remove + if (args.get(1).startsWith("+") || args.get(1).startsWith("-")) { + String change = args.get(1).substring(1); + if (!Util.isInteger(change, true)) { + user.sendMessage("admin.level.sethandicap.invalid-level"); + return false; + } + // Value is okay + } else { + // Check value + if (!Util.isInteger(args.get(1), true)) { + user.sendMessage("admin.level.sethandicap.invalid-level"); + return false; + } } // Check island island = getAddon().getIslands().getIsland(getWorld(), targetUUID); diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index e5e90f5..3d7dbdc 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -8,8 +8,12 @@ admin: parameters: "" description: "calculate the island level for player" sethandicap: - parameters: - description: "set the island handicap, usually the level of the starter island" + parameters: [+/-] + description: | + set or change the island *handicap* + e.g. +10 will remove 10 levels, + 30 will set handicap to 30, + -20 will add 20 levels changed: "&a Initial island handicap changed from [number] to [new_number]." invalid-level: "&c Invalid handicap. Use an integer." levelstatus: