diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java index 2951bbf..3df672b 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/StatisticRequirements.java @@ -2,11 +2,16 @@ // Created by BONNe // Copyright - 2021 // +// Enhanced by tastybento package world.bentobox.challenges.database.object.requirements; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import org.bukkit.Material; import org.bukkit.Statistic; import org.bukkit.entity.EntityType; @@ -15,8 +20,69 @@ import org.eclipse.jdt.annotation.Nullable; import com.google.gson.annotations.Expose; +/** + * Requirements for statistics based challenges + */ public class StatisticRequirements extends Requirements { + /** + * Record for this requirement + * @param Statistic statistic + * @param EntityType entity + * @param Material material + * @param Integer amount + * @param Boolean reduceStatistic + */ + public record StatisticRec(@Expose Statistic statistic, @Expose EntityType entity, @Expose Material material, + @Expose Integer amount, @Expose Boolean reduceStatistic) { + } + + /** + * Type of the statistic field. + * @deprecated Shifting to a list + */ + @Expose + @Nullable + private Statistic statistic; + + /** + * Type of entity for entity related statistics. + * @deprecated Shifting to a list + */ + @Expose + @Nullable + private EntityType entity; + + /** + * Type of material for block and item related statistics. + * @deprecated Shifting to a list + */ + @Expose + @Nullable + private Material material; + + /** + * Amount of the stats. + * @deprecated Shifting to a list + */ + @Expose + private Integer amount; + + /** + * Indicate that player statistic fields must be adjusted after completing challenges. + * @deprecated Shifting to a list + */ + @Expose + private Boolean reduceStatistic; + + /** + * List of statistics that must be done for this challenge + */ + @Expose + @Nullable + private List statisticList; + + /** * Constructor Requirements creates a new Requirements instance. */ @@ -34,12 +100,7 @@ public class StatisticRequirements extends Requirements public Requirements copy() { StatisticRequirements requirements = new StatisticRequirements(); - requirements.setStatistic(this.statistic); - requirements.setEntity(this.entity); - requirements.setMaterial(this.material); - requirements.setAmount(this.amount); - requirements.setReduceStatistic(this.reduceStatistic); - + requirements.setStatisticList(this.getRequiredStatistics()); return requirements; } @@ -47,183 +108,33 @@ public class StatisticRequirements extends Requirements @Override public boolean isValid() { - if (!super.isValid()) - { - return false; + // TODO - do something here? + return super.isValid(); + } + + /** + * @return the statisticList + */ + public List getRequiredStatistics() { + if (statisticList == null) { + statisticList = new ArrayList<>(); + // Convert old single statistic entries to new list of records + if (statistic != null) { + StatisticRec rec = new StatisticRec(this.statistic, this.entity, this.material, this.amount, + this.reduceStatistic); + statisticList.add(rec); + } } + return statisticList; + } - if (this.statistic == null) - { - return false; - } - - return switch (this.statistic.getType()) - { - case ITEM -> this.material != null && this.material.isItem(); - - case BLOCK -> this.material != null && this.material.isBlock(); - - case ENTITY -> this.entity != null; - - case UNTYPED -> true; - - }; - + /** + * @param value the statisticList to set + */ + public void setStatisticList(Collection value) { + // If value is null, assign null; otherwise, create a new ArrayList from value. + this.statisticList = (value == null) ? null : new ArrayList<>(value); } - // --------------------------------------------------------------------- -// Section: Getters and setters -// --------------------------------------------------------------------- - - - /** - * Gets statistic. - * - * @return the statistic - */ - @Nullable - public Statistic getStatistic() - { - return statistic; - } - - - /** - * Sets statistic. - * - * @param statistic the statistic - */ - public void setStatistic(@Nullable Statistic statistic) - { - this.statistic = statistic; - } - - - /** - * Gets entity. - * - * @return the entity - */ - @Nullable - public EntityType getEntity() - { - return entity; - } - - - /** - * Sets entity. - * - * @param entity the entity - */ - public void setEntity(@Nullable EntityType entity) - { - this.entity = entity; - } - - - /** - * Gets material. - * - * @return the material - */ - @Nullable - public Material getMaterial() - { - return material; - } - - - /** - * Sets material. - * - * @param material the material - */ - public void setMaterial(@Nullable Material material) - { - this.material = material; - } - - - /** - * Gets amount. - * - * @return the amount - */ - public int getAmount() - { - return amount; - } - - - /** - * Sets amount. - * - * @param amount the amount - */ - public void setAmount(int amount) - { - this.amount = amount; - } - - - /** - * Is reduce statistic boolean. - * - * @return the boolean - */ - public boolean isReduceStatistic() - { - return reduceStatistic; - } - - - /** - * Sets reduce statistic. - * - * @param reduceStatistic the reduce statistic - */ - public void setReduceStatistic(boolean reduceStatistic) - { - this.reduceStatistic = reduceStatistic; - } - - -// --------------------------------------------------------------------- -// Section: Variables -// --------------------------------------------------------------------- - - /** - * Type of the statistic field. - */ - @Expose - @Nullable - private Statistic statistic; - - /** - * Type of entity for entity related statistics. - */ - @Expose - @Nullable - private EntityType entity; - - /** - * Type of material for block and item related statistics. - */ - @Expose - @Nullable - private Material material; - - /** - * Amount of the stats. - */ - @Expose - private int amount; - - /** - * Indicate that player statistic fields must be adjusted after completing challenges. - */ - @Expose - private boolean reduceStatistic; } diff --git a/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java index ef99daa..85abe79 100644 --- a/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java +++ b/src/main/java/world/bentobox/challenges/managers/ChallengesImportManager.java @@ -50,6 +50,7 @@ import world.bentobox.challenges.database.object.requirements.InventoryRequireme import world.bentobox.challenges.database.object.requirements.IslandRequirements; import world.bentobox.challenges.database.object.requirements.OtherRequirements; import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec; import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -356,13 +357,12 @@ public class ChallengesImportManager case STATISTIC_TYPE -> { StatisticRequirements requirements = new StatisticRequirements(); challenge.setRequirements(requirements); - - requirements.setAmount(section.getInt("amount", 0)); - requirements.setReduceStatistic(section.getBoolean("reduce", false)); - - requirements.setStatistic(matchStatistic(section.getString("statistic"))); - requirements.setEntity(matchEntity(section.getString("entity"))); - requirements.setMaterial(matchMaterial(section.getString("material"))); + List list = new ArrayList<>(); + list.add(new StatisticRec(matchStatistic(section.getString("statistic")), + matchEntity(section.getString("entity")), matchMaterial(section.getString("material")), + section.getInt("amount", 0), section.getBoolean("reduce", false))); + // TODO: Add support for multiple stat challenge + requirements.setStatisticList(list); } } diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java index 8cedb18..5a5b95b 100644 --- a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.bukkit.Material; +import org.bukkit.Statistic; import org.bukkit.World; import org.bukkit.inventory.ItemStack; import org.eclipse.jdt.annotation.NonNull; @@ -29,6 +30,7 @@ import world.bentobox.challenges.database.object.requirements.InventoryRequireme import world.bentobox.challenges.database.object.requirements.IslandRequirements; import world.bentobox.challenges.database.object.requirements.OtherRequirements; import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec; import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.LevelStatus; @@ -467,7 +469,7 @@ public abstract class CommonPanel { } /** - * This method generates lore message for Statistic requirement. + * This method generates lore message for Statistic requirements. * * @param requirement Statistic Requirement. * @return Requirement lore message. @@ -475,48 +477,49 @@ public abstract class CommonPanel { private String generateStatisticChallenge(StatisticRequirements requirement) { final String reference = Constants.DESCRIPTIONS + "challenge.requirements.statistic."; - String statistic; - - if (requirement.getStatistic() == null) { + if (requirement.getRequiredStatistics().isEmpty()) { // Challenges by default comes with empty statistic field. return ""; } - switch (requirement.getStatistic().getType()) { - case UNTYPED -> statistic = this.user.getTranslationOrNothing(reference + "statistic", "[statistic]", - Utils.prettifyObject(requirement.getStatistic(), this.user), "[number]", - String.valueOf(requirement.getAmount())); + StringBuilder statistics = new StringBuilder(); + for (StatisticRec s : requirement.getRequiredStatistics()) { + String statistic = switch (s.statistic().getType()) { + case UNTYPED -> this.user.getTranslationOrNothing(reference + "statistic", "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[number]", String.valueOf(s.amount())); case ITEM, BLOCK -> { - if (requirement.getAmount() > 1) { - statistic = this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]", - Utils.prettifyObject(requirement.getStatistic(), this.user), "[number]", - String.valueOf(requirement.getAmount()), "[target]", - Utils.prettifyObject(requirement.getMaterial(), this.user)); + if (s.amount() > 1) { + yield this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[number]", String.valueOf(s.amount()), + "[target]", Utils.prettifyObject(s.material(), this.user)); } else { - statistic = this.user.getTranslationOrNothing(reference + "single-target", "[statistic]", - Utils.prettifyObject(requirement.getStatistic(), this.user), "[target]", - Utils.prettifyObject(requirement.getMaterial(), this.user)); + yield this.user.getTranslationOrNothing(reference + "single-target", "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[target]", + Utils.prettifyObject(s.material(), this.user)); } } case ENTITY -> { - if (requirement.getAmount() > 1) { - statistic = this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]", - Utils.prettifyObject(requirement.getStatistic(), this.user), "[number]", - String.valueOf(requirement.getAmount()), "[target]", - Utils.prettifyObject(requirement.getEntity(), this.user)); + if (s.amount() > 1) { + yield this.user.getTranslationOrNothing(reference + "multiple-target", "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[number]", String.valueOf(s.amount()), + "[target]", Utils.prettifyObject(s.entity(), this.user)); } else { - statistic = this.user.getTranslationOrNothing(reference + "single-target", "[statistic]", - Utils.prettifyObject(requirement.getStatistic(), this.user), "[target]", - Utils.prettifyObject(requirement.getEntity(), this.user)); + yield this.user.getTranslationOrNothing(reference + "single-target", "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[target]", + Utils.prettifyObject(s.entity(), this.user)); } } - default -> statistic = ""; + default -> ""; + }; + + String warning = s.reduceStatistic() ? this.user.getTranslationOrNothing(reference + "warning") + : ""; + statistics.append(this.user.getTranslationOrNothing(reference + "lore", "[statistic]", statistic, "[warning]", + warning)); + statistics.append("\n"); + } - - String warning = requirement.isReduceStatistic() ? this.user.getTranslationOrNothing(reference + "warning") - : ""; - - return this.user.getTranslationOrNothing(reference + "lore", "[statistic]", statistic, "[warning]", warning); + return statistics.toString(); } /** diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java index 83fc3cf..b680271 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java @@ -35,9 +35,6 @@ import world.bentobox.challenges.panel.ConversationUtils; import world.bentobox.challenges.panel.util.EnvironmentSelector; import world.bentobox.challenges.panel.util.ItemSelector; import world.bentobox.challenges.panel.util.MultiBlockSelector; -import world.bentobox.challenges.panel.util.SingleBlockSelector; -import world.bentobox.challenges.panel.util.SingleEntitySelector; -import world.bentobox.challenges.panel.util.StatisticSelector; import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -229,24 +226,8 @@ public class EditChallengePanel extends CommonPanel { * @param panelBuilder PanelBuilder where icons must be added. */ private void buildStatisticRequirementsPanel(PanelBuilder panelBuilder) { - panelBuilder.item(10, this.createRequirementButton(RequirementButton.STATISTIC)); - panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_STATISTIC)); - - panelBuilder.item(11, this.createRequirementButton(RequirementButton.STATISTIC_AMOUNT)); - - StatisticRequirements requirements = this.challenge.getRequirements(); - - if (requirements.getStatistic() != null) { - switch (requirements.getStatistic().getType()) { - case ITEM -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ITEMS)); - case BLOCK -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_BLOCKS)); - case ENTITY -> panelBuilder.item(13, this.createRequirementButton(RequirementButton.STATISTIC_ENTITIES)); - default -> { - } - } - } - - panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); + // Give user the ability to add or remove statistics + panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_STATISTICS)); } /** @@ -627,9 +608,9 @@ public class EditChallengePanel extends CommonPanel { case REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE, REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY -> { return this.createOtherRequirementButton(button); } - // Buttons for Statistic Requirements - case STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, STATISTIC_ENTITIES, STATISTIC_AMOUNT, REMOVE_STATISTIC -> { - return this.createStatisticRequirementButton(button); + // Statistics + case REQUIRED_STATISTICS -> { + return this.createStatisticsRequirementButton(button); } // Default behaviour. default -> { @@ -702,8 +683,8 @@ public class EditChallengePanel extends CommonPanel { description.add(this.user.getTranslation(reference + "title")); // Add Material Tags only requirements.getRequiredMaterialTags() - .forEach((block, count) -> description.add(this.user.getTranslation(reference + "list", - "[tag]", Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count)))); + .forEach((block, count) -> description.add(this.user.getTranslation(reference + "list", "[tag]", + Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count)))); } icon = new ItemStack(Material.STONE_BRICKS); @@ -724,8 +705,8 @@ public class EditChallengePanel extends CommonPanel { description.add(this.user.getTranslation(reference + "title")); // Add Material Tags only requirements.getRequiredEntityTypeTags() - .forEach((block, count) -> description.add(this.user.getTranslation(reference + "list", - "[tag]", Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count)))); + .forEach((block, count) -> description.add(this.user.getTranslation(reference + "list", "[tag]", + Utils.prettifyObject(block, this.user), "[number]", String.valueOf(count)))); } icon = new ItemStack(Material.ZOMBIE_HEAD); @@ -811,6 +792,62 @@ public class EditChallengePanel extends CommonPanel { .clickHandler(clickHandler).build(); } + /** + * This method creates buttons for inventory requirements menu. + * + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createStatisticsRequirementButton(RequirementButton button) { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + final StatisticRequirements requirements = this.challenge.getRequirements(); + switch (button) { + // Just one special statistic button right now + case REQUIRED_STATISTICS -> { + // TODO rename getStaticicList() to getRequiredStatistics() + if (requirements.getRequiredStatistics().isEmpty()) { + description.add(this.user.getTranslation(reference + "none")); + } else { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getRequiredStatistics().stream() + .sorted(Comparator.comparing(r -> r.statistic().getKey().getKey())) + // Just list the name of the statistic + .forEach(sr -> description.add(this.user.getTranslationOrNothing(reference + "list", "[name]", + Utils.prettifyObject(sr.statistic(), user)))); + } + + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + // Deal with adding and removing statistics in the MultiStatisticSelector class + ManageStatisticsPanel.open(this, requirements.getRequiredStatistics()); + return true; + }; + glow = false; + } + default -> { + // This should never need to be shown. Just for future expansion. + glow = false; + icon = new ItemStack(Material.PAPER); + clickHandler = null; + } + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + return new PanelItemBuilder().icon(icon).name(name).description(description).glow(glow) + .clickHandler(clickHandler).build(); + } + /** * This method creates buttons for inventory requirements menu. * @@ -1085,163 +1122,6 @@ public class EditChallengePanel extends CommonPanel { .clickHandler(clickHandler).build(); } - /** - * Creates a button for statistic requirements. - * - * @param button Button that must be created. - * @return PanelItem button. - */ - private PanelItem createStatisticRequirementButton(RequirementButton button) { - final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; - - final String name = this.user.getTranslation(reference + "name"); - final List description = new ArrayList<>(3); - description.add(this.user.getTranslation(reference + "description")); - - ItemStack icon; - boolean glow; - PanelItem.ClickHandler clickHandler; - - final StatisticRequirements requirements = this.challenge.getRequirements(); - - switch (button) { - case STATISTIC -> { - description.add(this.user.getTranslation(reference + "value", "[statistic]", - Utils.prettifyObject(requirements.getStatistic(), this.user))); - - icon = new ItemStack(requirements.getStatistic() == null ? Material.BARRIER : Material.PAPER); - clickHandler = (panel, user, clickType, slot) -> { - StatisticSelector.open(this.user, (status, statistic) -> { - if (status) { - requirements.setStatistic(statistic); - requirements.setMaterial(null); - requirements.setEntity(null); - requirements.setAmount(0); - } - - this.build(); - }); - return true; - }; - glow = false; - - description.add(""); - description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); - } - case STATISTIC_AMOUNT -> { - description.add(this.user.getTranslation(reference + "value", Constants.PARAMETER_NUMBER, - String.valueOf(requirements.getAmount()))); - icon = new ItemStack(Material.CHEST); - clickHandler = (panel, user, clickType, i) -> { - Consumer numberConsumer = number -> { - if (number != null) { - requirements.setAmount(number.intValue()); - } - - // reopen panel - this.build(); - }; - ConversationUtils.createNumericInput(numberConsumer, this.user, - this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), 0, Integer.MAX_VALUE); - - return true; - }; - glow = false; - - description.add(""); - description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); - } - case REMOVE_STATISTIC -> { - description.add( - this.user.getTranslation(reference + (requirements.isReduceStatistic() ? "enabled" : "disabled"))); - - icon = new ItemStack(Material.LEVER); - clickHandler = (panel, user, clickType, slot) -> { - requirements.setReduceStatistic(!requirements.isReduceStatistic()); - this.build(); - return true; - }; - glow = requirements.isReduceStatistic(); - - description.add(""); - description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); - } - case STATISTIC_BLOCKS -> { - description.add(this.user.getTranslation(reference + "value", "[block]", - Utils.prettifyObject(requirements.getMaterial(), this.user))); - - icon = requirements.getMaterial() == null ? new ItemStack(Material.BARRIER) - : new ItemStack(requirements.getMaterial()); - clickHandler = (panel, user, clickType, slot) -> { - SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.BLOCKS, (status, block) -> { - if (status) { - requirements.setMaterial(block); - } - - this.build(); - }); - - return true; - }; - glow = false; - - description.add(""); - description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); - } - case STATISTIC_ITEMS -> { - description.add(this.user.getTranslation(reference + "value", "[item]", - Utils.prettifyObject(requirements.getMaterial(), this.user))); - - icon = requirements.getMaterial() == null ? new ItemStack(Material.BARRIER) - : new ItemStack(requirements.getMaterial()); - clickHandler = (panel, user, clickType, slot) -> { - SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.ITEMS, (status, block) -> { - if (status) { - requirements.setMaterial(block); - } - - this.build(); - }); - - return true; - }; - glow = false; - - description.add(""); - description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); - } - case STATISTIC_ENTITIES -> { - description.add(this.user.getTranslation(reference + "value", "[entity]", - Utils.prettifyObject(requirements.getEntity(), this.user))); - - icon = requirements.getEntity() == null ? new ItemStack(Material.BARRIER) - : new ItemStack(PanelUtils.getEntityEgg(requirements.getEntity())); - clickHandler = (panel, user, clickType, slot) -> { - SingleEntitySelector.open(this.user, true, (status, entity) -> { - if (status) { - requirements.setEntity(entity); - } - - this.build(); - }); - - return true; - }; - glow = false; - - description.add(""); - description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); - } - default -> { - icon = new ItemStack(Material.PAPER); - clickHandler = null; - glow = false; - } - } - return new PanelItemBuilder().icon(icon).name(name).description(description).glow(glow) - .clickHandler(clickHandler).build(); - } - /** * This method creates buttons for rewards menu. * @@ -1791,11 +1671,13 @@ public class EditChallengePanel extends CommonPanel { /** * Represents different requirement buttons that are used in menus. */ - private enum RequirementButton { + public enum RequirementButton { REQUIRED_ENTITIES, REMOVE_ENTITIES, REQUIRED_BLOCKS, REMOVE_BLOCKS, SEARCH_RADIUS, REQUIRED_PERMISSIONS, REQUIRED_ITEMS, REMOVE_ITEMS, ADD_IGNORED_META, REMOVE_IGNORED_META, REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE, - REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY, STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, STATISTIC_ENTITIES, - STATISTIC_AMOUNT, REMOVE_STATISTIC, REQUIRED_MATERIALTAGS, REQUIRED_ENTITYTAGS, + REQUIRED_LEVEL, REQUIRED_MONEY, REMOVE_MONEY, STATISTIC_SHOW, STATISTIC, STATISTIC_BLOCKS, STATISTIC_ITEMS, + STATISTIC_ENTITIES, NEW_STATISTIC, + STATISTIC_AMOUNT, REMOVE_STATISTIC, REQUIRED_MATERIALTAGS, REQUIRED_ENTITYTAGS, REQUIRED_STATISTICS, + REMOVE_STATISTICS, } // --------------------------------------------------------------------- diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageStatisticsPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageStatisticsPanel.java new file mode 100644 index 0000000..199dbce --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageStatisticsPanel.java @@ -0,0 +1,616 @@ +package world.bentobox.challenges.panel.admin; + + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.inventory.ItemStack; +import org.eclipse.jdt.annotation.Nullable; + +import lv.id.bonne.panelutils.PanelUtils; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.panels.Panel; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec; +import world.bentobox.challenges.panel.CommonPagedPanel; +import world.bentobox.challenges.panel.CommonPanel; +import world.bentobox.challenges.panel.ConversationUtils; +import world.bentobox.challenges.panel.admin.EditChallengePanel.RequirementButton; +import world.bentobox.challenges.panel.util.SingleBlockSelector; +import world.bentobox.challenges.panel.util.SingleEntitySelector; +import world.bentobox.challenges.panel.util.StatisticSelector; +import world.bentobox.challenges.utils.Constants; +import world.bentobox.challenges.utils.Utils; + + +/** + * This class allows to edit material that are in required material map. + */ +public class ManageStatisticsPanel extends CommonPagedPanel +{ + + // --------------------------------------------------------------------- + // Section: Enums + // --------------------------------------------------------------------- + + /** + * Functional buttons in current GUI. + */ + private enum Button { + ADD_STATISTIC, REMOVE_STATISTIC + } + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * Contains selected stats. + */ + private final Set selectedStats; + + /** + * List of required statistics + */ + private final List statisticsList; + + /** + * Stores filtered items. + */ + private List filterElements; + + private ManageStatisticsPanel(CommonPanel parentGUI, List statisticsList) + { + super(parentGUI); + this.statisticsList = statisticsList; + + // Sort tags by their ordinal value. + this.statisticsList.sort(Comparator.comparing(tag -> tag.statistic().getKey().getKey())); + + this.selectedStats = new HashSet<>(); + + // Init without filters applied. + this.filterElements = this.statisticsList; + } + + + /** + * Open the Challenges Admin GUI. + */ + public static void open(CommonPanel parentGUI, List statisticsList) { + new ManageStatisticsPanel(parentGUI, statisticsList).build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method is called when filter value is updated. + */ + @Override + protected void updateFilters() + { + if (this.searchString == null || this.searchString.isBlank()) + { + this.filterElements = this.statisticsList; + } + else + { + this.filterElements = this.statisticsList.stream(). + filter(element -> { + // If element name is set and name contains search field, then do not filter out. + return element.statistic().getKey().getKey().toLowerCase(Locale.ENGLISH) + .contains(this.searchString.toLowerCase(Locale.ENGLISH)); + }). + distinct(). + collect(Collectors.toList()); + } + } + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + protected void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation(Constants.TITLE + "manage-statistics")); + + // Create nice border. + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD_STATISTIC)); + panelBuilder.item(5, this.createButton(Button.REMOVE_STATISTIC)); + // Fill the box with what is selected + this.populateElements(panelBuilder, this.filterElements); + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + final String reference = Constants.BUTTON + button.name().toLowerCase() + "."; + + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + PanelItem.ClickHandler clickHandler; + boolean glow; + + switch (button) + { + case ADD_STATISTIC -> { + icon = new ItemStack(Material.BUCKET); + clickHandler = (panel, user1, clickType, slot) -> + { + StatisticSelector.open(this.user, (status, statistic) -> + { + if (status) + { + StatisticRec newItem = new StatisticRec(statistic, null, null, 0, false); + this.statisticsList.add(newItem); + + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_STATISTIC -> { + + if (!this.selectedStats.isEmpty()) + { + description.add(this.user.getTranslation(reference + "title")); + this.selectedStats.forEach(stat -> + description.add(this.user.getTranslation(reference + "statistic_element", "[statistic]", + Utils.prettifyObject(stat.statistic(), this.user)))); + } + + icon = new ItemStack(Material.LAVA_BUCKET); + + clickHandler = (panel, user1, clickType, slot) -> + { + if (!this.selectedStats.isEmpty()) + { + this.statisticsList.removeAll(this.selectedStats); + this.selectedStats.clear(); + this.build(); + } + + return true; + }; + + glow = !this.selectedStats.isEmpty(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + + return new PanelItemBuilder(). + icon(icon). + name(name). + description(description). + clickHandler(clickHandler). + glow(glow). + build(); + } + + + /** + * This method creates button for given stat. + * @param rec material which button must be created. + * @return new Button for material. + */ + @Override + protected PanelItem createElementButton(StatisticRec rec) + { + final String reference = Constants.BUTTON + "statistic_element."; + + List description = new ArrayList<>(); + + // Show everything about this statistic + // Type is not shown to the user, just used to decide what to show + switch (rec.statistic().getType()) { + case BLOCK: + description.add(this.user.getTranslation(reference + "block", "[block]", + Utils.prettifyObject(rec.material(), this.user))); + break; + case ENTITY: + description.add(this.user.getTranslation(reference + "entity", "[entity]", + Utils.prettifyObject(rec.entity(), this.user))); + break; + case ITEM: + description.add(this.user.getTranslation(reference + "item", "[item]", + Utils.prettifyObject(rec.material(), this.user))); + break; + default: + break; + } + // Amount + description.add(this.user.getTranslation(reference + "amount", Constants.PARAMETER_NUMBER, + String.valueOf(Objects.requireNonNullElse(rec.amount(), 0)))); + // Removal + description.add(this.user.getTranslation(reference + "remove.name", "[value]", + this.user.getTranslation(reference + "remove.value." + + (Objects.requireNonNullElse(rec.reduceStatistic(), false) ? "enabled" : "disabled")))); + + if (this.selectedStats.contains(rec)) + { + description.add(this.user.getTranslation(reference + "selected")); + } + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "left-click-to-choose")); + + if (this.selectedStats.contains(rec)) + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-deselect")); + } + else + { + description.add(this.user.getTranslation(Constants.TIPS + "right-click-to-select")); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation(reference + "name", "[statistic]", + Utils.prettifyObject(rec.statistic(), this.user))) + .icon(getStatisticIcon(rec.statistic())). + description(description). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (clickType.isRightClick()) + { + if (!this.selectedStats.add(rec)) + { + // Remove material if it is already selected + this.selectedStats.remove(rec); + } + + this.build(); + } + else + { + // Left click + this.buildStatisticPanel(rec); + } + return true; + }). + glow(this.selectedStats.contains(rec)). + build(); + } + + /** + * Get an icon for a Statistic. Hand selected! + * @param stat Statistic + * @return ItemStack icon + */ + public static ItemStack getStatisticIcon(Statistic stat) { + return switch (stat) { + case ANIMALS_BRED -> new ItemStack(Material.WHEAT); + case ARMOR_CLEANED -> new ItemStack(Material.LEATHER_CHESTPLATE); + case AVIATE_ONE_CM -> new ItemStack(Material.ELYTRA); + case BANNER_CLEANED -> new ItemStack(Material.RED_BANNER); + case BEACON_INTERACTION -> new ItemStack(Material.BEACON); + case BELL_RING -> new ItemStack(Material.BELL); + case BOAT_ONE_CM -> new ItemStack(Material.OAK_BOAT); + case BREAK_ITEM -> new ItemStack(Material.AMETHYST_SHARD); + case BREWINGSTAND_INTERACTION -> new ItemStack(Material.BREWING_STAND); + case CAKE_SLICES_EATEN -> new ItemStack(Material.CAKE); + case CAULDRON_FILLED -> new ItemStack(Material.CAULDRON); + case CAULDRON_USED -> new ItemStack(Material.CAULDRON); + case CHEST_OPENED -> new ItemStack(Material.CHEST); + case CLEAN_SHULKER_BOX -> new ItemStack(Material.SHULKER_BOX); + case CLIMB_ONE_CM -> new ItemStack(Material.LADDER); + case CRAFTING_TABLE_INTERACTION -> new ItemStack(Material.CRAFTING_TABLE); + case CRAFT_ITEM -> new ItemStack(Material.CRAFTING_TABLE); + case CROUCH_ONE_CM -> new ItemStack(Material.PAPER); + case DAMAGE_ABSORBED -> new ItemStack(Material.IRON_SWORD); + case DAMAGE_BLOCKED_BY_SHIELD -> new ItemStack(Material.SHIELD); + case DAMAGE_DEALT -> new ItemStack(Material.DIAMOND_SWORD); + case DAMAGE_DEALT_ABSORBED -> new ItemStack(Material.GOLDEN_SWORD); + case DAMAGE_DEALT_RESISTED -> new ItemStack(Material.WOODEN_SWORD); + case DAMAGE_RESISTED -> new ItemStack(Material.IRON_HELMET); + case DAMAGE_TAKEN -> new ItemStack(Material.IRON_LEGGINGS); + case DEATHS -> new ItemStack(Material.OBSIDIAN); + case DISPENSER_INSPECTED -> new ItemStack(Material.DISPENSER); + case DROP -> new ItemStack(Material.PAPER); + case DROPPER_INSPECTED -> new ItemStack(Material.DROPPER); + case DROP_COUNT -> new ItemStack(Material.PAPER); + case ENDERCHEST_OPENED -> new ItemStack(Material.ENDER_CHEST); + case ENTITY_KILLED_BY -> new ItemStack(Material.BOW); + case FALL_ONE_CM -> new ItemStack(Material.PAPER); + case FISH_CAUGHT -> new ItemStack(Material.FISHING_ROD); + case FLOWER_POTTED -> new ItemStack(Material.FLOWER_POT); + case FLY_ONE_CM -> new ItemStack(Material.ELYTRA); + case FURNACE_INTERACTION -> new ItemStack(Material.FURNACE); + case HOPPER_INSPECTED -> new ItemStack(Material.HOPPER); + case HORSE_ONE_CM -> new ItemStack(Material.IRON_HORSE_ARMOR); + case INTERACT_WITH_ANVIL -> new ItemStack(Material.ANVIL); + case INTERACT_WITH_BLAST_FURNACE -> new ItemStack(Material.BLAST_FURNACE); + case INTERACT_WITH_CAMPFIRE -> new ItemStack(Material.CAMPFIRE); + case INTERACT_WITH_CARTOGRAPHY_TABLE -> new ItemStack(Material.CARTOGRAPHY_TABLE); + case INTERACT_WITH_GRINDSTONE -> new ItemStack(Material.GRINDSTONE); + case INTERACT_WITH_LECTERN -> new ItemStack(Material.LECTERN); + case INTERACT_WITH_LOOM -> new ItemStack(Material.LOOM); + case INTERACT_WITH_SMITHING_TABLE -> new ItemStack(Material.SMITHING_TABLE); + case INTERACT_WITH_SMOKER -> new ItemStack(Material.SMOKER); + case INTERACT_WITH_STONECUTTER -> new ItemStack(Material.STONECUTTER); + case ITEM_ENCHANTED -> new ItemStack(Material.ENCHANTED_GOLDEN_APPLE); + case JUMP -> new ItemStack(Material.RABBIT_FOOT); + case KILL_ENTITY -> new ItemStack(Material.DIAMOND_SWORD); + case LEAVE_GAME -> new ItemStack(Material.PAPER); + case MINECART_ONE_CM -> new ItemStack(Material.MINECART); + case MINE_BLOCK -> new ItemStack(Material.STONE); + case MOB_KILLS -> new ItemStack(Material.PAPER); + case NOTEBLOCK_PLAYED -> new ItemStack(Material.NOTE_BLOCK); + case NOTEBLOCK_TUNED -> new ItemStack(Material.NOTE_BLOCK); + case OPEN_BARREL -> new ItemStack(Material.BARREL); + case PICKUP -> new ItemStack(Material.PAPER); + case PIG_ONE_CM -> new ItemStack(Material.PIG_SPAWN_EGG); + case PLAYER_KILLS -> new ItemStack(Material.PLAYER_HEAD); + case PLAY_ONE_MINUTE -> new ItemStack(Material.CLOCK); + case RAID_TRIGGER -> new ItemStack(Material.OMINOUS_BOTTLE); + case RAID_WIN -> new ItemStack(Material.GOLD_INGOT); + case RECORD_PLAYED -> new ItemStack(Material.MUSIC_DISC_BLOCKS); + case SHULKER_BOX_OPENED -> new ItemStack(Material.SHULKER_BOX); + case SLEEP_IN_BED -> new ItemStack(Material.RED_BED); + case SNEAK_TIME -> new ItemStack(Material.PAPER); + case SPRINT_ONE_CM -> new ItemStack(Material.PAPER); + case STRIDER_ONE_CM -> new ItemStack(Material.STRIDER_SPAWN_EGG); + case SWIM_ONE_CM -> new ItemStack(Material.WATER_BUCKET); + case TALKED_TO_VILLAGER -> new ItemStack(Material.EMERALD); + case TARGET_HIT -> new ItemStack(Material.TARGET); + case TIME_SINCE_DEATH -> new ItemStack(Material.CLOCK); + case TIME_SINCE_REST -> new ItemStack(Material.CLOCK); + case TOTAL_WORLD_TIME -> new ItemStack(Material.CLOCK); + case TRADED_WITH_VILLAGER -> new ItemStack(Material.EMERALD); + case TRAPPED_CHEST_TRIGGERED -> new ItemStack(Material.TRAPPED_CHEST); + case USE_ITEM -> new ItemStack(Material.STICK); + case WALK_ONE_CM -> new ItemStack(Material.LEATHER_BOOTS); + case WALK_ON_WATER_ONE_CM -> new ItemStack(Material.LILY_PAD); + case WALK_UNDER_WATER_ONE_CM -> new ItemStack(Material.WATER_BUCKET); + default -> new ItemStack(Material.PAPER); + + }; + } + + private Panel buildStatisticPanel(StatisticRec req) { + + PanelBuilder panelBuilder = new PanelBuilder().user(this.user); + + panelBuilder.name(this.user.getTranslation(Constants.TITLE + "statistic-selector")); + + PanelUtils.fillBorder(panelBuilder); + + panelBuilder.item(10, this.createStatisticRequirementButton(RequirementButton.STATISTIC, req)); + panelBuilder.item(19, this.createStatisticRequirementButton(RequirementButton.REMOVE_STATISTIC, req)); + + panelBuilder.item(11, this.createStatisticRequirementButton(RequirementButton.STATISTIC_AMOUNT, req)); + switch (req.statistic().getType()) { + case BLOCK: + panelBuilder.item(13, this.createStatisticRequirementButton(RequirementButton.STATISTIC_BLOCKS, req)); + break; + case ENTITY: + panelBuilder.item(13, this.createStatisticRequirementButton(RequirementButton.STATISTIC_ENTITIES, req)); + break; + case ITEM: + panelBuilder.item(13, this.createStatisticRequirementButton(RequirementButton.STATISTIC_ITEMS, req)); + break; + case UNTYPED: + break; + default: + break; + + } + panelBuilder.item(25, this.createStatisticRequirementButton(RequirementButton.REQUIRED_PERMISSIONS, req)); + panelBuilder.item(44, this.returnButton); + return panelBuilder.build(); + } + + /** + * Creates a button for statistic requirements. + * + * @param button Button that must be created. + * @param req + * @return PanelItem button. + */ + private PanelItem createStatisticRequirementButton(RequirementButton button, StatisticRec req) { + + final String reference = Constants.BUTTON + button.name().toLowerCase(Locale.ENGLISH) + "."; + final String name = this.user.getTranslation(reference + "name"); + final List description = new ArrayList<>(3); + description.add(this.user.getTranslation(reference + "description")); + + ItemStack icon; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) { + case STATISTIC -> { + description.add(this.user.getTranslation(reference + "value", "[statistic]", + Utils.prettifyObject(req.statistic(), this.user))); + + icon = new ItemStack(req.statistic() == null ? Material.BARRIER : Material.PAPER); + clickHandler = (panel, user, clickType, slot) -> { + StatisticSelector.open(this.user, (status, statistic) -> { + if (status) { + // Replace the old with the new + statisticsList.removeIf(sr -> sr.equals(req)); + statisticsList.add(new StatisticRec(statistic, null, null, 0, false)); + } + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_AMOUNT -> { + description.add(this.user.getTranslation(reference + "value", Constants.PARAMETER_NUMBER, + String.valueOf(req.amount()))); + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, i) -> { + Consumer numberConsumer = number -> { + if (number != null) { + // Replace the old with the new + statisticsList.removeIf(sr -> sr.equals(req)); + statisticsList.add(new StatisticRec(req.statistic(), req.entity(), req.material(), + number.intValue(), req.reduceStatistic())); + } + + // reopen panel + this.build(); + }; + ConversationUtils.createNumericInput(numberConsumer, this.user, + this.user.getTranslation(Constants.CONVERSATIONS + "input-number"), 0, Integer.MAX_VALUE); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case REMOVE_STATISTIC -> { + description.add(this.user.getTranslation(reference + (req.reduceStatistic() ? "enabled" : "disabled"))); + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + // Replace the old with the new + statisticsList.removeIf(sr -> sr.equals(req)); + statisticsList.add(new StatisticRec(req.statistic(), req.entity(), req.material(), req.amount(), + !req.reduceStatistic())); + + this.build(); + return true; + }; + glow = req.reduceStatistic(); + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); + } + case STATISTIC_BLOCKS -> { + description.add(this.user.getTranslation(reference + "value", "[block]", + Utils.prettifyObject(req.material(), this.user))); + + icon = req.material() == null ? new ItemStack(Material.BARRIER) : new ItemStack(req.material()); + clickHandler = (panel, user, clickType, slot) -> { + SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.BLOCKS, (status, block) -> { + if (status) { + // Replace the old with the new + statisticsList.removeIf(sr -> sr.equals(req)); + statisticsList.add(new StatisticRec(req.statistic(), req.entity(), block, req.amount(), + req.reduceStatistic())); + + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_ITEMS -> { + description.add(this.user.getTranslation(reference + "value", "[item]", + Utils.prettifyObject(req.material(), this.user))); + + icon = req.material() == null ? new ItemStack(Material.BARRIER) : new ItemStack(req.material()); + clickHandler = (panel, user, clickType, slot) -> { + SingleBlockSelector.open(this.user, SingleBlockSelector.Mode.ITEMS, (status, block) -> { + if (status) { + // Replace the old with the new + statisticsList.removeIf(sr -> sr.equals(req)); + statisticsList.add(new StatisticRec(req.statistic(), req.entity(), block, req.amount(), + req.reduceStatistic())); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + case STATISTIC_ENTITIES -> { + description.add(this.user.getTranslation(reference + "value", "[entity]", + Utils.prettifyObject(req.entity(), this.user))); + + icon = req.entity() == null ? new ItemStack(Material.BARRIER) + : new ItemStack(PanelUtils.getEntityEgg(req.entity())); + clickHandler = (panel, user, clickType, slot) -> { + SingleEntitySelector.open(this.user, true, (status, entity) -> { + if (status) { + // Replace the old with the new + statisticsList.removeIf(sr -> sr.equals(req)); + statisticsList.add(new StatisticRec(req.statistic(), entity, req.material(), req.amount(), + req.reduceStatistic())); + } + + this.build(); + }); + + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-change")); + } + default -> { + icon = new ItemStack(Material.PAPER); + clickHandler = null; + glow = false; + } + } + return new PanelItemBuilder().icon(icon).name(name).description(description).glow(glow) + .clickHandler(clickHandler).build(); + } + + +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java b/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java index 5d4a519..2fcf957 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java +++ b/src/main/java/world/bentobox/challenges/panel/util/StatisticSelector.java @@ -17,6 +17,7 @@ import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.panel.admin.ManageStatisticsPanel; import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -132,13 +133,11 @@ public class StatisticSelector extends PagedSelector return new PanelItemBuilder(). name(this.user.getTranslation(reference + "name", "[statistic]", Utils.prettifyObject(statistic, this.user))). - icon(Material.PAPER). - description(description). - clickHandler((panel, user1, clickType, slot) -> { - this.consumer.accept(true, statistic); - return true; - }). - build(); + icon(ManageStatisticsPanel.getStatisticIcon(statistic)).description(description) + .clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, statistic); + return true; + }).build(); } diff --git a/src/main/java/world/bentobox/challenges/panel/util/UnifiedMultiSelector.java b/src/main/java/world/bentobox/challenges/panel/util/UnifiedMultiSelector.java index 2a344d0..5f2e1f8 100644 --- a/src/main/java/world/bentobox/challenges/panel/util/UnifiedMultiSelector.java +++ b/src/main/java/world/bentobox/challenges/panel/util/UnifiedMultiSelector.java @@ -57,6 +57,20 @@ public abstract class UnifiedMultiSelector extends PagedSelector { this.filterElements = this.elements; } + protected UnifiedMultiSelector(User user, Mode mode, List elements, + BiConsumer> consumer) { + super(user); + this.mode = mode; + this.consumer = consumer; + this.selectedElements = new HashSet<>(); + //If the elements are passed to the subclass in the constructor + this.elements = elements; + // Sort elements using the provided string representation. + this.elements.sort(Comparator.comparing(this::elementToString)); + // Start with the full list as the filtered list. + this.filterElements = this.elements; + } + /** * Subclasses must return the complete list of available elements. */ diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 244ed94..3b2824d 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -3,6 +3,7 @@ package world.bentobox.challenges.tasks; import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -48,6 +49,7 @@ import world.bentobox.challenges.database.object.requirements.InventoryRequireme import world.bentobox.challenges.database.object.requirements.IslandRequirements; import world.bentobox.challenges.database.object.requirements.OtherRequirements; import world.bentobox.challenges.database.object.requirements.StatisticRequirements; +import world.bentobox.challenges.database.object.requirements.StatisticRequirements.StatisticRec; import world.bentobox.challenges.managers.ChallengesManager; import world.bentobox.challenges.utils.Constants; import world.bentobox.challenges.utils.Utils; @@ -475,63 +477,58 @@ public class TryToComplete } case STATISTIC_TYPE -> { StatisticRequirements requirements = this.challenge.getRequirements(); + for (StatisticRec s : requirements.getRequiredStatistics()) { + if (s.reduceStatistic() && s.statistic() != null) { + int removeAmount = result.getFactor() * s.amount(); - if (requirements.isReduceStatistic() && requirements.getStatistic() != null) { - int removeAmount = result.getFactor() * requirements.getAmount(); - - // Start to remove from player who called the completion. - switch (requirements.getStatistic().getType()) - { - case UNTYPED -> { - int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic()); - - if (removeAmount >= statistic) { - this.user.getPlayer().setStatistic(requirements.getStatistic(), 0); - removeAmount -= statistic; - } else { - this.user.getPlayer().setStatistic(requirements.getStatistic(), statistic - removeAmount); - removeAmount = 0; - } - } - case ITEM, BLOCK -> { - if (requirements.getMaterial() == null) { - // Just a sanity check. Material cannot be null at this point of code. - removeAmount = 0; - } else { - int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic(), - requirements.getMaterial()); + // Start to remove from player who called the completion. + switch (s.statistic().getType()) + { + case UNTYPED -> { + int statistic = this.user.getPlayer().getStatistic(s.statistic()); if (removeAmount >= statistic) { - this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(), - 0); + this.user.getPlayer().setStatistic(s.statistic(), 0); removeAmount -= statistic; } else { - this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getMaterial(), - statistic - removeAmount); + this.user.getPlayer().setStatistic(s.statistic(), statistic - removeAmount); removeAmount = 0; } } - } - case ENTITY -> { - if (requirements.getEntity() == null) { - // Just a sanity check. Entity cannot be null at this point of code. - removeAmount = 0; - } else { - int statistic = this.user.getPlayer().getStatistic(requirements.getStatistic(), - requirements.getEntity()); - - if (removeAmount >= statistic) { - this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(), - 0); - removeAmount -= statistic; - } else { - this.user.getPlayer().setStatistic(requirements.getStatistic(), requirements.getEntity(), - statistic - removeAmount); + case ITEM, BLOCK -> { + if (s.material() == null) { + // Just a sanity check. Material cannot be null at this point of code. removeAmount = 0; + } else { + int statistic = this.user.getPlayer().getStatistic(s.statistic(), s.material()); + + if (removeAmount >= statistic) { + this.user.getPlayer().setStatistic(s.statistic(), s.material(), 0); + removeAmount -= statistic; + } else { + this.user.getPlayer().setStatistic(s.statistic(), s.material(), + statistic - removeAmount); + removeAmount = 0; + } } } - } - } + case ENTITY -> { + if (s.entity() == null) { + // Just a sanity check. Entity cannot be null at this point of code. + removeAmount = 0; + } else { + int statistic = this.user.getPlayer().getStatistic(s.statistic(), s.entity()); + + if (removeAmount >= statistic) { + this.user.getPlayer().setStatistic(s.statistic(), s.entity(), 0); + removeAmount -= statistic; + } else { + this.user.getPlayer().setStatistic(s.statistic(), s.entity(), statistic - removeAmount); + removeAmount = 0; + } + } + } + } // If challenges are in sync with all island members, then punish others too. if (this.addon.getChallengesSettings().isStoreAsIslandData()) @@ -553,64 +550,62 @@ public class TryToComplete continue; } - switch (Objects.requireNonNull(requirements.getStatistic()).getType()) { + switch (Objects.requireNonNull(s.statistic()).getType()) { case UNTYPED -> { - int statistic = player.getStatistic(requirements.getStatistic()); + int statistic = player.getStatistic(s.statistic()); if (removeAmount >= statistic) { removeAmount -= statistic; - player.setStatistic(requirements.getStatistic(), 0); + player.setStatistic(s.statistic(), 0); } else { - player.setStatistic(requirements.getStatistic(), statistic - removeAmount); + player.setStatistic(s.statistic(), statistic - removeAmount); removeAmount = 0; } } case ITEM, BLOCK -> { - if (requirements.getMaterial() == null) + if (s.material() == null) { // Just a sanity check. Entity cannot be null at this point of code. removeAmount = 0; } else { - int statistic = player.getStatistic(requirements.getStatistic(), - requirements.getMaterial()); + int statistic = player.getStatistic(s.statistic(), s.material()); if (removeAmount >= statistic) { removeAmount -= statistic; - player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), 0); + player.setStatistic(s.statistic(), s.material(), 0); } else { - player.setStatistic(requirements.getStatistic(), requirements.getMaterial(), + player.setStatistic(s.statistic(), s.material(), statistic - removeAmount); removeAmount = 0; } } } case ENTITY -> { - if (requirements.getEntity() == null) + if (s.entity() == null) { // Just a sanity check. Entity cannot be null at this point of code. removeAmount = 0; } else { - int statistic = player.getStatistic(requirements.getStatistic(), - requirements.getEntity()); + int statistic = player.getStatistic(s.statistic(), s.entity()); if (removeAmount >= statistic) { removeAmount -= statistic; - player.setStatistic(requirements.getStatistic(), requirements.getEntity(), 0); + player.setStatistic(s.statistic(), s.entity(), 0); } else { - player.setStatistic(requirements.getStatistic(), requirements.getEntity(), + player.setStatistic(s.statistic(), s.entity(), statistic - removeAmount); removeAmount = 0; } @@ -619,6 +614,7 @@ public class TryToComplete } } } + } } } } @@ -1470,54 +1466,58 @@ public class TryToComplete int currentValue; - if (requirements.getStatistic() == null) { + if (requirements.getRequiredStatistics().isEmpty()) { // Sanity check. return EMPTY_RESULT; } + List cr = new ArrayList<>(); + // Check all requirements + for (StatisticRec s : requirements.getRequiredStatistics()) { - switch (Objects.requireNonNull(requirements.getStatistic()).getType()) - { - case UNTYPED -> - currentValue = this.manager.getStatisticData(this.user, this.world, requirements.getStatistic()); - case ITEM, BLOCK -> currentValue = this.manager.getStatisticData(this.user, this.world, - requirements.getStatistic(), requirements.getMaterial()); - case ENTITY -> currentValue = this.manager.getStatisticData(this.user, this.world, requirements.getStatistic(), - requirements.getEntity()); - default -> currentValue = 0; - } - - if (currentValue < requirements.getAmount()) { - switch (Objects.requireNonNull(requirements.getStatistic()).getType()) + switch (Objects.requireNonNull(s.statistic()).getType()) { - case ITEM, BLOCK -> { - Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met-material", - TextVariables.NUMBER, String.valueOf(requirements.getAmount()), "[statistic]", - Utils.prettifyObject(requirements.getStatistic(), this.user), "[material]", - Utils.prettifyObject(requirements.getMaterial(), this.user), Constants.PARAMETER_VALUE, - String.valueOf(currentValue)); + case UNTYPED -> currentValue = this.manager.getStatisticData(this.user, this.world, s.statistic()); + case ITEM, BLOCK -> + currentValue = this.manager.getStatisticData(this.user, this.world, s.statistic(), s.material()); + case ENTITY -> + currentValue = this.manager.getStatisticData(this.user, this.world, s.statistic(), s.entity()); + default -> currentValue = 0; } - case ENTITY -> { - Utils.sendMessage(this.user, - this.world, Constants.ERRORS + "requirement-not-met-entity", TextVariables.NUMBER, - String.valueOf(requirements.getAmount()), "[statistic]", - Utils.prettifyObject(requirements.getStatistic(), this.user), "[entity]", - Utils.prettifyObject(requirements.getEntity(), this.user), Constants.PARAMETER_VALUE, - String.valueOf(currentValue)); - } - default -> { - Utils.sendMessage(this.user, - this.world, Constants.ERRORS + "requirement-not-met", TextVariables.NUMBER, - String.valueOf(requirements.getAmount()), "[statistic]", - Utils.prettifyObject(requirements.getStatistic(), this.user), Constants.PARAMETER_VALUE, - String.valueOf(currentValue)); - } - } - } else { - factor = requirements.getAmount() == 0 ? factor : Math.min(factor, currentValue / requirements.getAmount()); - return new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor); + if (currentValue < s.amount()) { + switch (Objects.requireNonNull(s.statistic()).getType()) { + case ITEM, BLOCK -> { + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met-material", + TextVariables.NUMBER, String.valueOf(s.amount()), "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[material]", + Utils.prettifyObject(s.material(), this.user), Constants.PARAMETER_VALUE, + String.valueOf(currentValue)); + } + case ENTITY -> { + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met-entity", + TextVariables.NUMBER, String.valueOf(s.amount()), "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), "[entity]", + Utils.prettifyObject(s.entity(), this.user), Constants.PARAMETER_VALUE, + String.valueOf(currentValue)); + } + default -> { + Utils.sendMessage(this.user, this.world, Constants.ERRORS + "requirement-not-met", + TextVariables.NUMBER, String.valueOf(s.amount()), "[statistic]", + Utils.prettifyObject(s.statistic(), this.user), Constants.PARAMETER_VALUE, + String.valueOf(currentValue)); + } + } + } else { + factor = s.amount() == 0 ? factor : Math.min(factor, currentValue / s.amount()); + // Store result + cr.add(new ChallengeResult().setMeetsRequirements().setCompleteFactor(factor)); + } + } + // Check results -- all must pass + if (cr.stream().allMatch(result -> result.meetsRequirements)) { + // Return any of them, because they pass + return cr.getFirst(); } - return EMPTY_RESULT; } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 1d2803d..01eb9a1 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -63,6 +63,7 @@ challenges: manage-block-groups: "&0&l Manage Block Groups" manage-entity-groups: "&0&l Manage Entity Groups" manage-entities: "&0&l Manage Entities" + manage-statistics: "&0&l Manage Statistics" type-selector: "&0&l Challenge Type Selector" item-selector: "&0&l Item Selector" block-selector: "&0&l Block Selector" @@ -212,7 +213,8 @@ challenges: name: "&f&l Deployment" description: |- &7 Toggle whether the challenge is - &7 deployed and can be completed by users. + &7 deployed and can be completed + &7 by users. enabled: "&2 Enabled" disabled: "&c Disabled" name: @@ -265,7 +267,8 @@ challenges: name: "&f&l Required Permissions" description: |- &7 Allows you to change the required - &7 permissions for this challenge to be completed. + &7 permissions for this challenge to + &7 be completed. title: "&7 Permissions: " permission: " &8 - [permission]" none: "&7 No permissions are set." @@ -273,15 +276,16 @@ challenges: name: "&f&l Remove Entities" description: |- &7 Toggle whether required entities will - &7 be removed from the world after completing - &7 the challenge. + &7 be removed from the world after + &7 completing the challenge. enabled: "&2 Enabled" disabled: "&c Disabled" required_entities: name: "&f&l Required Entities" description: |- &7 Allows you to change the required - &7 entities for this challenge to be completed. + &7 entities for this challenge to be + &7 completed. title: "&7 Entities: " list: " &8 - [number] x [entity]" none: "&7 No entities have been added." @@ -289,18 +293,28 @@ challenges: name: "&f&l Remove Blocks" description: |- &7 Toggle whether required blocks will - &7 be removed from the world after completing - &7 the challenge. + &7 be removed from the world after + &7 completing the challenge. enabled: "&2 Enabled" disabled: "&c Disabled" required_blocks: name: "&f&l Required Blocks" description: |- &7 Allows you to change the required - &7 blocks for this challenge to be completed. + &7 blocks for this challenge to be + &7 completed. title: "&7 Blocks: " list: " &8 - [number] x [block]" none: "&7 No blocks have been added." + required_statistics: + name: "&f&l Required Statistics" + description: |- + &7 Allows you to change the required + &7 statistics for this challenge to be + &7 completed. + title: "&7 Statistics: " + list: " &8 - [name]" + none: "&7 No statistics have been added." search_radius: name: "&f&l Search Radius" description: |- @@ -312,8 +326,8 @@ challenges: name: "&f&l Remove Items" description: |- &7 Toggle whether required items will - &7 be removed from the inventory after completing - &7 the challenge. + &7 be removed from the inventory after + &7 completing the challenge. enabled: "&2 Enabled" disabled: "&c Disabled" required_items: @@ -327,8 +341,9 @@ challenges: add_ignored_meta: name: "&f&l Add Ignore Metadata" description: |- - &7 Allows you to specify which items should ignore - &7 any metadata assigned to them. + &7 Allows you to specify which items + &7 should ignore any metadata + &7 assigned to them. title: "&7 Items: " list: " &8 - [number] x [item]" none: "&7 No items have been added." @@ -340,28 +355,31 @@ challenges: remove_experience: name: "&f&l Remove Experience" description: |- - &7 Toggle whether the required experience will - &7 be removed from the player after completing + &7 Toggle whether the required + &7 experience will be removed + &7 from the player after completing &7 the challenge. enabled: "&2 Enabled" disabled: "&c Disabled" required_experience: name: "&f&l Required Experience" description: |- - &7 Allows you to change the required experience for - &7 the player. + &7 Allows you to change the required + &7 experience for the player. value: "&7 Current experience: &e [number]" required_level: name: "&f&l Required Island Level" description: |- - &7 Allows you to change the required island level + &7 Allows you to change the + &7 required island level &7 for the challenge. value: "&7 Current level: &e [number]" required_materialtags: name: "&f&l Required Block Groups" description: |- &7 Allows you to change the required - &7 block groups for this challenge to be completed. + &7 block groups for this challenge to + &7 be completed. title: "&7 Block group: " list: " &8 - [number] x [tag]" none: "&7 No block groups have been added." @@ -369,57 +387,71 @@ challenges: name: "&f&l Required Entity Groups" description: |- &7 Allows you to change the required - &7 entity groups for this challenge to be completed. + &7 entity groups for this challenge to + &7 be completed. title: "&7 Entity group: " list: " &8 - [number] x [tag]" none: "&7 No entity groups have been added." remove_money: name: "&f&l Remove Money" description: |- - &7 Toggle whether the required money will - &7 be removed from the player's account after completing - &7 the challenge. + &7 Toggle whether the required + &7 money will be removed from + &7 the player's account after + &7 completing the challenge. enabled: "&2 Enabled" disabled: "&c Disabled" required_money: name: "&f&l Required Money" description: |- - &7 Allows you to change the required money on the player's + &7 Allows you to change the required + &7 money on the player's &7 account for the challenge. value: "&7 Current value: &e [number]" statistic: name: "&f&l Statistic" description: |- - &7 Allows you to change the statistic type that is - &7 checked for this challenge. + &7 Allows you to change the statistic + &7 type that is checked for + &7 this challenge. value: "&7 Current value: &e [statistic]" statistic_amount: name: "&f&l Target Value" description: |- - &7 Allows you to change the target statistic value + &7 Allows you to change the target + &7 statistic value &7 that must be met. value: "&7 Current value: &e [number]" - remove_statistic: - name: "&f&l Reduce Statistic" + add_statistic: + name: "&f&l Add Statistic" description: |- - &7 Toggle whether the statistic value will - &7 be reduced after completing the challenge. + &7 Allows you to add a new + &7 statistic to the list. + remove_statistic: + name: "&f&l Remove Statistic" + description: |- + &7 Allows you to remove + &7 selected statistics + &7 from the list. enabled: "&2 Enabled" disabled: "&c Disabled" statistic_blocks: name: "&f&l Target Block" description: |- - &7 Allows you to change the target block for the statistic. + &7 Allows you to change the target + &7 block for the statistic. value: "&7 Current block: &e [block]" statistic_items: name: "&f&l Target Item" description: |- - &7 Allows you to change the target item for the statistic. + &7 Allows you to change the target + &7 item for the statistic. value: "&7 Current item: &e [item]" statistic_entities: name: "&f&l Target Entity" description: |- - &7 Allows you to change the target entity for the statistic. + &7 Allows you to change the target + &7 entity for the statistic. value: "&7 Current entity: &e [entity]" reward_text: name: "&f&l Reward Text" @@ -430,7 +462,8 @@ challenges: repeat_reward_text: name: "&f&l Repeat Reward Text" description: |- - &7 Specifies the repeat reward text for the challenge. + &7 Specifies the repeat reward + &7 text for the challenge. &7 Color codes must be applied. value: "&7 Current text:" reward_items: @@ -443,21 +476,22 @@ challenges: repeat_reward_items: name: "&f&l Repeat Reward Items" description: |- - &7 Allows you to change the repeat reward items for this challenge. + &7 Allows you to change the repeat + &7 reward items for this challenge. title: "&7 Items: " list: " &8 - [number] x [item]" none: "&7 No items have been added." reward_experience: name: "&f&l Reward Experience" description: |- - &7 Allows you to change the reward experience for - &7 the player. + &7 Allows you to change the reward + & experience for the player. value: "&7 Reward experience: &e [number]" repeat_reward_experience: name: "&f&l Repeat Reward Experience" description: |- - &7 Allows you to change the repeat reward experience - &7 for the player. + &7 Allows you to change the repeat + &7 reward experience for the player. value: "&7 Reward experience: &e [number]" reward_money: name: "&f&l Reward Money" @@ -467,8 +501,8 @@ challenges: repeat_reward_money: name: "&f&l Repeat Reward Money" description: |- - &7 Allows you to change the repeat reward money - &7 for the challenge. + &7 Allows you to change the repeat + &7 reward money for the challenge. value: "&7 Current value: &e [number]" reward_commands: name: "&f&l Reward Commands" @@ -511,8 +545,9 @@ challenges: cool_down: name: "&f&l Cool Down" description: |- - &7 Allows you to change the cooldown period (in seconds) - &7 that must elapse between repeatable challenge completions. + &7 Allows you to change the cooldown + &7 period (in seconds) that must elapse + &7 between repeatable challenge completions. value: "&7 Current value: &e [time]" challenges: name: "&f&l Challenges" @@ -522,66 +557,79 @@ challenges: waiver_amount: name: "&f&l Waiver Amount" description: |- - &7 Allows you to set the number of challenges that can - &7 be left uncompleted to unlock the next level. + &7 Allows you to set the number of challenges + &7 that can be left uncompleted to unlock + &7 the next level. value: "&7 Current value: &e [number]" add_challenges: name: "&f&l Add Challenge(-s)" description: |- - &7 Allows you to select and add challenges to the level. + &7 Allows you to select and add + &7 challenges to the level. remove_challenges: name: "&f&l Remove Challenge(-s)" description: |- - &7 Allows you to select and remove challenges from the level. + &7 Allows you to select and remove + &7 challenges from the level. reset_on_new: name: "&f&l Reset On New" description: |- - &7 Toggle whether challenges should be reset when a user leaves + &7 Toggle whether challenges should + & be reset when a user leaves &7 their island or creates a new one. enabled: "&2 Enabled" disabled: "&c Disabled" broadcast: name: "&f&l Broadcast" description: |- - &7 Broadcasts the first-time completion of a challenge or level to everyone. + &7 Broadcasts the first-time completion + &7 of a challenge or level to everyone. enabled: "&2 Enabled" disabled: "&c Disabled" remove_completed: name: "&f&l Hide Completed" description: |- - &7 Hides completed challenges from the menu. + &7 Hides completed challenges + &7 from the menu. enabled: "&2 Enabled" disabled: "&c Disabled" glow_completed: name: "&f&l Glow Completed" description: |- - &7 Applies an enchantment glow to completed challenges. + &7 Applies an enchantment glow + &7 to completed challenges. enabled: "&2 Enabled" disabled: "&c Disabled" store_history: name: "&f&l Store History" description: |- - &7 Stores internal history each time a challenge is completed. - &7 Currently viewable only in the database. + &7 Stores internal history each time + &7 a challenge is completed. + &7 Currently viewable only in the + &7 database. enabled: "&2 Enabled" disabled: "&c Disabled" data_per_island: name: "&f&l Store Per Island" description: |- - &7 Stores completed challenges on a per-island basis. - &7 Progress will be shared with all team members. + &7 Stores completed challenges + &7 on a per-island basis. + &7 Progress will be shared + &7 with all team members. enabled: "&2 Enabled" disabled: "&c Disabled" show_title: name: "&f&l Show Title" description: |- - &7 Displays a title when a challenge or level is completed. + &7 Displays a title when a challenge + &7 or level is completed. enabled: "&2 Enabled" disabled: "&c Disabled" gamemode_gui: name: "&f&l GameMode Selection GUI" description: |- - &7 Enables a single GUI accessible via the /challenges command. + &7 Enables a single GUI accessible + &7 via the /challenges command. &7 (Requires server restart.) enabled: "&2 Enabled" disabled: "&c Disabled" @@ -593,20 +641,26 @@ challenges: purge_history: name: "&f&l History Lifetime" description: |- - &7 Specifies the number of days that history data is stored + &7 Specifies the number of days + &7 that history data is stored &7 in user data. - &7 A value of 0 means the data will not be removed. + &7 A value of 0 means the data will + &7 not be removed. value: "&7 Current value: &e [number]" title_showtime: name: "&f&l Title Showtime" description: |- - &7 Number of ticks the title will be displayed to the player. + &7 Number of ticks the title will + & be displayed to the player. value: "&7 Current value: &e [number]" active_world_list: name: "&f&l Show Only Active World" description: |- - &7 If the GameMode Selection GUI is enabled, this setting determines - &7 whether the GUI displays the GameMode selection or challenges for the current world. + &7 If the GameMode Selection GUI is + &7 enabled, this setting determines + &7 whether the GUI displays the + &7 GameMode selection or challenges + &7 for the current world. &7 (Requires server restart.) enabled: "&2 Enabled" disabled: "&c Disabled" @@ -622,13 +676,15 @@ challenges: include_undeployed: name: "&f&l Include Undeployed Challenges" description: |- - &7 Indicates whether undeployed challenges should be counted towards level completion. + &7 Indicates whether undeployed challenges + &7 should be counted towards level completion. enabled: "&2 Enabled" disabled: "&c Disabled" download: name: "&f&l Download Libraries" description: |- - &7 Manually updates the available challenges libraries. + &7 Manually updates the available + &7 challenges libraries. enabled: "&2 With cache clear" disabled: "&c Without cache clear" player: @@ -728,20 +784,24 @@ challenges: inventory_type: name: "&f&l Inventory Type" description: |- - &7 Challenge that checks items in the player's inventory. + &7 Challenge that checks items + &7 in the player's inventory. island_type: name: "&f&l Island Type" description: |- - &7 Challenge that checks blocks or entities around the player. + &7 Challenge that checks blocks + &7 or entities around the player. other_type: name: "&f&l Other Type" description: |- - &7 Challenge that utilizes plugins or add-on features, + &7 Challenge that utilizes plugins + &7 or add-on features, &7 such as level and money. statistic_type: name: "&f&l Statistic Type" description: |- - &7 Challenge that checks the player's statistic data. + &7 Challenge that checks the + &7 player's statistic data. save: name: "&f&l Save" description: |- @@ -761,7 +821,17 @@ challenges: element: "&8 - [element]" statistic_element: name: "&f&l [statistic]" + amount: "&7 Target Value: &e [number]" + remove: + name: "&7 Reduce Statistic: [value]" + value: + enabled: "&c Enabled" + disabled: "&2 Disabled" + block: "&7 Target Block: &e [block]" + item: "&7 Target Item: &e [item]" + entity: "&7 Target Entity: &e [entity]" description: "[description]" + selected: "&2 Selected" environment_element: name: "&f&l [environment]" description: "[description]"