From 08d15611388f4e8ea8bca46f2e25a39f86345abb Mon Sep 17 00:00:00 2001 From: BONNe Date: Fri, 24 Sep 2021 17:16:33 +0300 Subject: [PATCH] Implement an option to set which item type will ignore metadata per challenge. Fixes #261 Fixes #252 --- .../requirements/InventoryRequirements.java | 41 ++++++- .../challenges/panel/CommonPanel.java | 8 +- .../panel/admin/EditChallengePanel.java | 108 +++++++++++++++++- .../panel/admin/EditLevelPanel.java | 3 +- .../challenges/tasks/TryToComplete.java | 42 +++---- .../bentobox/challenges/utils/Utils.java | 31 +---- src/main/resources/locales/en-US.yml | 17 +++ src/main/resources/locales/lv.yml | 19 ++- .../bentobox/challenges/utils/UtilsTest.java | 25 +--- 9 files changed, 212 insertions(+), 82 deletions(-) diff --git a/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java b/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java index 64741b4..7c1f27d 100644 --- a/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java +++ b/src/main/java/world/bentobox/challenges/database/object/requirements/InventoryRequirements.java @@ -7,12 +7,10 @@ package world.bentobox.challenges.database.object.requirements; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; +import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import com.google.gson.annotations.Expose; @@ -81,6 +79,34 @@ public class InventoryRequirements extends Requirements } + /** + * Gets ignore meta data. + * + * @return the ignore meta data + */ + public Set getIgnoreMetaData() + { + if (this.ignoreMetaData == null) + { + // Fixes null-pointer, that should not be possible, but may be. + this.ignoreMetaData = new HashSet<>(); + } + + return this.ignoreMetaData; + } + + + /** + * Sets ignore meta data. + * + * @param ignoreMetaData the ignore meta data + */ + public void setIgnoreMetaData(Set ignoreMetaData) + { + this.ignoreMetaData = ignoreMetaData; + } + + // --------------------------------------------------------------------- // Section: Other methods // --------------------------------------------------------------------- @@ -114,6 +140,7 @@ public class InventoryRequirements extends Requirements map(ItemStack::clone). collect(Collectors.toCollection(() -> new ArrayList<>(this.requiredItems.size())))); clone.setTakeItems(this.takeItems); + clone.setIgnoreMetaData(new HashSet<>(this.ignoreMetaData)); return clone; } @@ -129,6 +156,12 @@ public class InventoryRequirements extends Requirements @Expose private List requiredItems = new ArrayList<>(); + /** + * Set of item stacks that should ignore metadata. + */ + @Expose + private Set ignoreMetaData = new HashSet<>(); + /** * Boolean that indicate if challenge completion should remove items from inventory. */ diff --git a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java index 337f740..5211e08 100644 --- a/src/main/java/world/bentobox/challenges/panel/CommonPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/CommonPanel.java @@ -436,7 +436,7 @@ public abstract class CommonPanel { StringBuilder builder = new StringBuilder(); builder.append(this.user.getTranslationOrNothing(reference + "item-title")); - Utils.groupEqualItems(requirement.getRequiredItems()).stream(). + Utils.groupEqualItems(requirement.getRequiredItems(), requirement.getIgnoreMetaData()).stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> { @@ -659,7 +659,7 @@ public abstract class CommonPanel { StringBuilder builder = new StringBuilder(); builder.append(this.user.getTranslationOrNothing(reference + "item-title")); - Utils.groupEqualItems(challenge.getRepeatItemReward()).stream(). + Utils.groupEqualItems(challenge.getRepeatItemReward(), Collections.emptySet()).stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> { @@ -760,7 +760,7 @@ public abstract class CommonPanel { StringBuilder builder = new StringBuilder(); builder.append(this.user.getTranslationOrNothing(reference + "item-title")); - Utils.groupEqualItems(challenge.getRewardItems()).stream(). + Utils.groupEqualItems(challenge.getRewardItems(), Collections.emptySet()).stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> { @@ -974,7 +974,7 @@ public abstract class CommonPanel { StringBuilder builder = new StringBuilder(); builder.append(this.user.getTranslationOrNothing(reference + "item-title")); - Utils.groupEqualItems(level.getRewardItems()).stream(). + Utils.groupEqualItems(level.getRewardItems(), Collections.emptySet()).stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> { 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 96edbfb..0781e9c 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengePanel.java @@ -4,6 +4,7 @@ package world.bentobox.challenges.panel.admin; import java.time.Duration; import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.World; @@ -207,6 +208,16 @@ public class EditChallengePanel extends CommonPanel panelBuilder.item(10, this.createRequirementButton(RequirementButton.REQUIRED_ITEMS)); panelBuilder.item(19, this.createRequirementButton(RequirementButton.REMOVE_ITEMS)); + if (!this.challenge.getRequirements().getRequiredItems().isEmpty()) + { + panelBuilder.item(12, this.createRequirementButton(RequirementButton.ADD_IGNORED_META)); + + if (!this.challenge.getRequirements().getIgnoreMetaData().isEmpty()) + { + panelBuilder.item(21, this.createRequirementButton(RequirementButton.REMOVE_IGNORED_META)); + } + } + panelBuilder.item(25, this.createRequirementButton(RequirementButton.REQUIRED_PERMISSIONS)); } @@ -683,7 +694,7 @@ public class EditChallengePanel extends CommonPanel return this.createIslandRequirementButton(button); } // Buttons for Inventory Requirements - case REQUIRED_ITEMS, REMOVE_ITEMS -> { + case REQUIRED_ITEMS, REMOVE_ITEMS, ADD_IGNORED_META, REMOVE_IGNORED_META -> { return this.createInventoryRequirementButton(button); } // Buttons for Other Requirements @@ -882,7 +893,8 @@ public class EditChallengePanel extends CommonPanel { description.add(this.user.getTranslation(reference + "title")); - Utils.groupEqualItems(requirements.getRequiredItems()).stream(). + Utils.groupEqualItems(requirements.getRequiredItems(), requirements.getIgnoreMetaData()). + stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> description.add(this.user.getTranslationOrNothing(reference + "list", @@ -923,6 +935,90 @@ public class EditChallengePanel extends CommonPanel description.add(""); description.add(this.user.getTranslation(Constants.TIPS + "click-to-toggle")); } + case ADD_IGNORED_META -> { + if (requirements.getIgnoreMetaData().isEmpty()) + { + description.add(this.user.getTranslation(reference + "none")); + } + else + { + description.add(this.user.getTranslation(reference + "title")); + + requirements.getIgnoreMetaData().stream(). + sorted(Comparator.comparing(Material::name)). + forEach(itemStack -> + description.add(this.user.getTranslationOrNothing(reference + "list", + "[item]", Utils.prettifyObject(itemStack, this.user)))); + } + + icon = new ItemStack(Material.GREEN_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (requirements.getRequiredItems().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + requirements.getRequiredItems().stream(). + map(ItemStack::getType). + forEach(collection::remove); + collection.addAll(requirements.getIgnoreMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + requirements.setIgnoreMetaData(new HashSet<>(materials)); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-add")); + } + case REMOVE_IGNORED_META -> { + icon = new ItemStack(Material.RED_SHULKER_BOX); + + clickHandler = (panel, user, clickType, slot) -> { + if (requirements.getIgnoreMetaData().isEmpty()) + { + // Do nothing if no requirements are set. + return true; + } + + // Allow choosing only from inventory items. + Set collection = Arrays.stream(Material.values()).collect(Collectors.toSet()); + collection.removeAll(requirements.getIgnoreMetaData()); + + MultiBlockSelector.open(this.user, + MultiBlockSelector.Mode.ANY, + collection, + (status, materials) -> + { + if (status) + { + requirements.getIgnoreMetaData().removeAll(materials); + } + + this.build(); + }); + return true; + }; + glow = false; + + description.add(""); + description.add(this.user.getTranslation(Constants.TIPS + "click-to-remove")); + } default -> { icon = new ItemStack(Material.PAPER); clickHandler = null; @@ -1346,7 +1442,8 @@ public class EditChallengePanel extends CommonPanel { description.add(this.user.getTranslation(reference + "title")); - Utils.groupEqualItems(this.challenge.getRewardItems()).stream(). + Utils.groupEqualItems(this.challenge.getRewardItems(), Collections.emptySet()). + stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> description.add(this.user.getTranslationOrNothing(reference + "list", @@ -1597,7 +1694,8 @@ public class EditChallengePanel extends CommonPanel { description.add(this.user.getTranslation(reference + "title")); - Utils.groupEqualItems(this.challenge.getRepeatItemReward()).stream(). + Utils.groupEqualItems(this.challenge.getRepeatItemReward(), Collections.emptySet()). + stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> description.add(this.user.getTranslationOrNothing(reference + "list", @@ -1869,6 +1967,8 @@ public class EditChallengePanel extends CommonPanel REQUIRED_PERMISSIONS, REQUIRED_ITEMS, REMOVE_ITEMS, + ADD_IGNORED_META, + REMOVE_IGNORED_META, REQUIRED_EXPERIENCE, REMOVE_EXPERIENCE, REQUIRED_LEVEL, diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java index 1dd5289..5460405 100644 --- a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelPanel.java @@ -387,7 +387,8 @@ public class EditLevelPanel extends CommonPagedPanel { description.add(this.user.getTranslation(reference + "title")); - Utils.groupEqualItems(this.challengeLevel.getRewardItems()).stream(). + Utils.groupEqualItems(this.challengeLevel.getRewardItems(), Collections.emptySet()). + stream(). sorted(Comparator.comparing(ItemStack::getType)). forEach(itemStack -> description.add(this.user.getTranslationOrNothing(reference + "list", diff --git a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java index 4536e08..475c74d 100644 --- a/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/tasks/TryToComplete.java @@ -888,30 +888,29 @@ public class TryToComplete // Players in creative game mode has got all items. No point to search for them. if (this.user.getPlayer().getGameMode() != GameMode.CREATIVE) { - requiredItems = Utils.groupEqualItems(this.getInventoryRequirements().getRequiredItems()); + requiredItems = Utils.groupEqualItems(this.getInventoryRequirements().getRequiredItems(), + this.getInventoryRequirements().getIgnoreMetaData()); // Check if all required items are in players inventory. for (ItemStack required : requiredItems) { int numInInventory; - if (Utils.canIgnoreMeta(required.getType())) + if (this.getInventoryRequirements().getIgnoreMetaData().contains(required.getType())) { - numInInventory = - Arrays.stream(this.user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.getType().equals(required.getType())). - mapToInt(ItemStack::getAmount). - sum(); + numInInventory = Arrays.stream(this.user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.getType().equals(required.getType())). + mapToInt(ItemStack::getAmount). + sum(); } else { - numInInventory = - Arrays.stream(this.user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.isSimilar(required)). - mapToInt(ItemStack::getAmount). - sum(); + numInInventory = Arrays.stream(this.user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.isSimilar(required)). + mapToInt(ItemStack::getAmount). + sum(); } if (numInInventory < required.getAmount()) @@ -952,22 +951,23 @@ public class TryToComplete int amountToBeRemoved = required.getAmount() * factor; List itemsInInventory; - if (Utils.canIgnoreMeta(required.getType())) + if (this.getInventoryRequirements().getIgnoreMetaData().contains(required.getType())) { // Use collecting method that ignores item meta. itemsInInventory = Arrays.stream(user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.getType().equals(required.getType())). - collect(Collectors.toList()); + filter(Objects::nonNull). + filter(i -> i.getType().equals(required.getType())). + collect(Collectors.toList()); } else { // Use collecting method that compares item meta. itemsInInventory = Arrays.stream(user.getInventory().getContents()). - filter(Objects::nonNull). - filter(i -> i.isSimilar(required)). - collect(Collectors.toList()); + filter(Objects::nonNull). + filter(i -> i.isSimilar(required)). + collect(Collectors.toList()); } + for (ItemStack itemStack : itemsInInventory) { if (amountToBeRemoved > 0) diff --git a/src/main/java/world/bentobox/challenges/utils/Utils.java b/src/main/java/world/bentobox/challenges/utils/Utils.java index b4cd99e..9e6b0a2 100644 --- a/src/main/java/world/bentobox/challenges/utils/Utils.java +++ b/src/main/java/world/bentobox/challenges/utils/Utils.java @@ -5,6 +5,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.Set; import org.bukkit.ChatColor; import org.bukkit.Material; @@ -15,9 +16,7 @@ import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.*; import org.bukkit.potion.PotionData; -import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import org.bukkit.potion.PotionType; import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.BentoBox; @@ -37,7 +36,7 @@ public class Utils * @param requiredItems Input item list * @return List that contains unique items that cannot be grouped. */ - public static List groupEqualItems(List requiredItems) + public static List groupEqualItems(List requiredItems, Set ignoreMetaData) { List returnItems = new ArrayList<>(requiredItems.size()); @@ -55,8 +54,7 @@ public class Utils ItemStack required = returnItems.get(i); // Merge items which meta can be ignored or is similar to item in required list. - if (Utils.canIgnoreMeta(item.getType()) && item.getType().equals(required.getType()) || - required.isSimilar(item)) + if (ignoreMetaData.contains(item.getType()) && item.getType().equals(required.getType())) { required.setAmount(required.getAmount() + item.getAmount()); isUnique = false; @@ -76,29 +74,6 @@ public class Utils } - /** - * This method returns if meta data of these items can be ignored. It means, that items will be searched - * and merged by they type instead of using ItemStack#isSimilar(ItemStack) method. - * - * This limits custom Challenges a lot. It comes from ASkyBlock times, and that is the reason why it is - * still here. It would be a great Challenge that could be completed by collecting 4 books, that cannot - * be crafted. Unfortunately, this prevents it. - * The same happens with firework rockets, enchanted books and filled maps. - * In future it should be able to specify, which items meta should be ignored when adding item in required - * item list. - * - * @param material Material that need to be checked. - * @return True if material meta can be ignored, otherwise false. - */ - public static boolean canIgnoreMeta(Material material) - { - return material.equals(Material.FIREWORK_ROCKET) || - material.equals(Material.ENCHANTED_BOOK) || - material.equals(Material.WRITTEN_BOOK) || - material.equals(Material.FILLED_MAP); - } - - /** * This method transforms given World into GameMode name. If world is not a GameMode * world then it returns null. diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 4ca469f..dbb53b4 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -330,6 +330,23 @@ challenges: title: "&7 Items: " list: " &8 - [item]" none: "&7 Items are not added." + add_ignored_meta: + name: "&f&l Add Ignore Metadata" + description: |- + &7 Allows to add which + &7 items should ignore + &7 any meta-data that + &7 is assigned to them. + title: "&7 Items: " + list: " &8 - [item]" + none: "&7 Items are not added." + remove_ignored_meta: + name: "&f&l Remove Ignore Metadata" + description: |- + &7 Allows to remove which + &7 items should ignore + &7 any meta-data that + &7 is assigned to them. remove_experience: name: "&f&l Remove Experience" description: |- diff --git a/src/main/resources/locales/lv.yml b/src/main/resources/locales/lv.yml index 5790911..935e12a 100644 --- a/src/main/resources/locales/lv.yml +++ b/src/main/resources/locales/lv.yml @@ -325,7 +325,24 @@ challenges: &7 uzdevuma izpildei. title: "&7 Priekšmeti: " list: " &8 - [item]" - none: "&7 Priekšmeti nav uzstādītas." + none: "&7 Priekšmeti nav uzstādīti." + add_ignored_meta: + name: "&f&l Pievienot Meta-datu Ignorēšanu" + description: |- + &7 Ļauj uzstādīt, kuriem + &7 priekšmetiem drīkst + &7 ignorēt meta-datus, kā + &7 bojājumi, vārdi, uzlabojumi. + title: "&7 Priekšmeti: " + list: " &8 - [item]" + none: "&7 Priekšmeti nav uzstādīti." + remove_ignored_meta: + name: "&f&l Noņemt Meta-datu Ignorēšanu" + description: |- + &7 Ļauj noņemt, kuriem + &7 priekšmetiem drīkst + &7 ignorēt meta-datus, kā + &7 bojājumi, vārdi, uzlabojumi. remove_experience: name: "&f&l Noņemt Pieredzi" description: |- diff --git a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java index a89873f..a3f5aaa 100644 --- a/src/test/java/world/bentobox/challenges/utils/UtilsTest.java +++ b/src/test/java/world/bentobox/challenges/utils/UtilsTest.java @@ -82,15 +82,15 @@ public class UtilsTest { } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItemsEmpty() { - assertTrue(Utils.groupEqualItems(Collections.emptyList()).isEmpty()); + assertTrue(Utils.groupEqualItems(Collections.emptyList(), Collections.emptySet()).isEmpty()); } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItems() { @@ -112,14 +112,14 @@ public class UtilsTest { when(is2.clone()).thenReturn(is); requiredItems.add(is2); } - List list = Utils.groupEqualItems(requiredItems); + List list = Utils.groupEqualItems(requiredItems, Collections.emptySet()); // Result should be two stacks stack of 64 doors and 36 doors assertEquals(1, list.size()); verify(is, times(9)).setAmount(2); } /** - * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List)}. + * Test method for {@link world.bentobox.challenges.utils.Utils#groupEqualItems(java.util.List, java.util.Set)}. */ @Test public void testGroupEqualItemsUnique() { @@ -141,24 +141,12 @@ public class UtilsTest { when(is2.clone()).thenReturn(is); requiredItems.add(is2); } - List list = Utils.groupEqualItems(requiredItems); + List list = Utils.groupEqualItems(requiredItems, Collections.emptySet()); // Result should be two stacks stack of 64 doors and 36 doors assertEquals(10, list.size()); verify(is, never()).setAmount(2); } - /** - * Test method for {@link world.bentobox.challenges.utils.Utils#canIgnoreMeta(org.bukkit.Material)}. - */ - @Test - public void testCanIgnoreMeta() { - assertTrue(Utils.canIgnoreMeta(Material.FIREWORK_ROCKET)); - assertTrue(Utils.canIgnoreMeta(Material.ENCHANTED_BOOK)); - assertTrue(Utils.canIgnoreMeta(Material.WRITTEN_BOOK)); - assertTrue(Utils.canIgnoreMeta(Material.FILLED_MAP)); - assertFalse(Utils.canIgnoreMeta(Material.CHISELED_RED_SANDSTONE)); - } - /** * Test method for {@link world.bentobox.challenges.utils.Utils#getGameMode(org.bukkit.World)}. */ @@ -195,5 +183,4 @@ public class UtilsTest { assertEquals(VisibilityMode.VISIBLE, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.HIDDEN)); assertEquals(VisibilityMode.HIDDEN, Utils.getPreviousValue(VisibilityMode.values(), VisibilityMode.TOGGLEABLE)); } - }