diff --git a/addon.yml b/addon.yml index b9a53ff..36ede7f 100755 --- a/addon.yml +++ b/addon.yml @@ -5,7 +5,7 @@ version: 0.1 authors: tastybento permissions: - bskyblock.island.challenges: + bskyblock.challenges: description: Let the player use the /challenges command default: true bskyblock.admin.challenges: diff --git a/locales/en-US.yml b/locales/en-US.yml index b5f967c..5c88e25 100755 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -7,6 +7,8 @@ # Tastybento: maintainer challenges: + parameters: "[Level]" + description: "Open the challenges menu" complete: "Complete" exp-reward: "Exp reward" first-time-rewards: "First time reward(s)" diff --git a/pom.xml b/pom.xml index fd9ce64..9244450 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - 4.0.0 0.0.1-SNAPSHOT @@ -55,28 +56,27 @@ bskyblock.addon Level - 0.0.1-SNAPSHOT provided - - org.mockito - mockito-all - 1.10.19 - test - - - org.powermock - powermock-module-junit4 - ${powermock.version} - test - - - org.powermock - powermock-api-mockito - ${powermock.version} - test - - + + org.mockito + mockito-all + 1.10.19 + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + diff --git a/src/main/java/bskyblock/addon/challenges/ChallengesAddon.java b/src/main/java/bskyblock/addon/challenges/ChallengesAddon.java index 05b73f6..8f52fd4 100644 --- a/src/main/java/bskyblock/addon/challenges/ChallengesAddon.java +++ b/src/main/java/bskyblock/addon/challenges/ChallengesAddon.java @@ -4,7 +4,6 @@ import org.bukkit.Bukkit; import bskyblock.addon.challenges.commands.ChallengesCommand; import bskyblock.addon.challenges.commands.admin.ChallengesAdminCommand; -import bskyblock.addon.challenges.config.PluginConfig; import us.tastybento.bskyblock.api.addons.Addon; /** @@ -18,8 +17,6 @@ public class ChallengesAddon extends Addon { @Override public void onEnable() { - // Load the plugin's config - new PluginConfig(this); // Check if it is enabled - it might be loaded, but not enabled. if (getBSkyBlock() == null || !getBSkyBlock().isEnabled()) { Bukkit.getLogger().severe("BSkyBlock is not available or disabled!"); @@ -40,6 +37,9 @@ public class ChallengesAddon extends Addon { @Override public void onDisable(){ + if (challengesManager != null) { + challengesManager.save(false); + } } public ChallengesManager getChallengesManager() { diff --git a/src/main/java/bskyblock/addon/challenges/ChallengesManager.java b/src/main/java/bskyblock/addon/challenges/ChallengesManager.java index 1623eb6..706e46d 100644 --- a/src/main/java/bskyblock/addon/challenges/ChallengesManager.java +++ b/src/main/java/bskyblock/addon/challenges/ChallengesManager.java @@ -1,6 +1,8 @@ package bskyblock.addon.challenges; import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; @@ -8,9 +10,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; -import org.bukkit.Bukkit; +import org.apache.commons.lang.WordUtils; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.inventory.Inventory; @@ -20,35 +23,75 @@ import bskyblock.addon.challenges.commands.admin.SurroundChallengeBuilder; import bskyblock.addon.challenges.database.object.ChallengeLevels; import bskyblock.addon.challenges.database.object.Challenges; import bskyblock.addon.challenges.database.object.Challenges.ChallengeType; +import bskyblock.addon.challenges.database.object.PlayerData; import bskyblock.addon.challenges.panel.ChallengesPanels; -import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.api.configuration.BSBConfig; import us.tastybento.bskyblock.api.user.User; +import us.tastybento.bskyblock.database.BSBdatab; import us.tastybento.bskyblock.util.Util; public class ChallengesManager { public static final String FREE = "Free"; - private LinkedHashMap> challengeList; + private Map> challengeMap; private BSBConfig chConfig; private BSBConfig lvConfig; + private BSBdatab players; private ChallengesPanels challengesPanels; + private Map playerData; + private ChallengesAddon addon; public ChallengesManager(ChallengesAddon addon) { + this.addon = addon; // Set up the configs - chConfig = new BSBConfig(addon, Challenges.class); - lvConfig = new BSBConfig(addon, ChallengeLevels.class); - challengeList = new LinkedHashMap<>(); + chConfig = new BSBConfig<>(addon, Challenges.class); + lvConfig = new BSBConfig<>(addon, ChallengeLevels.class); + // Players is where all the player history will be stored + players = new BSBdatab<>(addon, PlayerData.class); + // Cache of challenges + challengeMap = new LinkedHashMap<>(); // Start panels challengesPanels = new ChallengesPanels(addon, this); + // Cache of player data + playerData = new HashMap<>(); load(); } - public long checkChallengeTimes(User user, Challenges challenge) { - // TODO Auto-generated method stub - return 0; + /** + * Load player from database into the cache or create new player data + * @param user - user to add + */ + private void addPlayer(User user) { + if (playerData.containsKey(user.getUniqueId())) { + return; + } + // The player is not in the cache + // Check if the player exists in the database + if (players.objectExists(user.getUniqueId().toString())) { + // Load player from database + PlayerData data = players.loadObject(user.getUniqueId().toString()); + // Store in cache + playerData.put(user.getUniqueId(), data); + } else { + // Create the player data + PlayerData pd = new PlayerData(user.getUniqueId().toString()); + players.saveObject(pd); + // Add to cache + playerData.put(user.getUniqueId(), pd); + } } - + + /** + * Check how many times a player has done a challenge before + * @param user - user + * @param challenge - challenge + * @return - number of times + */ + public long checkChallengeTimes(User user, Challenges challenge) { + addPlayer(user); + return playerData.get(user.getUniqueId()).getTimes(challenge.getUniqueId()); + } + /** * Creates a simple example description of the requirements * @param user - user of this command @@ -56,6 +99,7 @@ public class ChallengesManager { * @return Description list */ private List createDescription(User user, List requiredItems) { + addPlayer(user); List result = new ArrayList<>(); result.add(user.getTranslation("challenges.admin.create.description")); for (ItemStack item : requiredItems) { @@ -63,13 +107,14 @@ public class ChallengesManager { } return result; } - + /** * Creates an inventory challenge * @param user - the user who is making the challenge * @param inventory - the inventory that will be used to make the challenge */ public boolean createInvChallenge(User user, Inventory inventory) { + addPlayer(user); if (inventory.getContents().length == 0) { return false; } @@ -95,9 +140,7 @@ public class ChallengesManager { if (item != null) { Map residual = user.getInventory().addItem(item); // Drop any residual items at the foot of the player - residual.forEach((k, v) -> { - user.getWorld().dropItem(user.getLocation(), v); - }); + residual.forEach((k, v) -> user.getWorld().dropItem(user.getLocation(), v)); } }); @@ -109,7 +152,7 @@ public class ChallengesManager { user.sendRawMessage("Success"); return true; } - + /** * Create a surrounding challenge * @param challengeInfo - info on the challenge from the builder @@ -145,17 +188,17 @@ public class ChallengesManager { */ public List getAllChallengesList() { List result = new ArrayList<>(); - challengeList.values().forEach(ch -> ch.forEach(c -> result.add(c.getUniqueId()))); + challengeMap.values().forEach(ch -> ch.forEach(c -> result.add(c.getUniqueId()))); return result; } - + /** * Get challenge by name * @param name - unique name of challenge * @return - challenge or null if it does not exist */ public Challenges getChallenge(String name) { - for (Set ch : challengeList.values()) { + for (Set ch : challengeMap.values()) { Optional challenge = ch.stream().filter(c -> c.getUniqueId().equalsIgnoreCase(name)).findFirst(); if (challenge.isPresent()) { return challenge.get(); @@ -166,16 +209,28 @@ public class ChallengesManager { /** * Get the status on every level - * @param user - * @return Level name, how many challenges still to do on which level + * @param user - user + * @return Level status - how many challenges still to do on which level */ public List getChallengeLevelStatus(User user) { + addPlayer(user); + PlayerData pd = playerData.get(user.getUniqueId()); List result = new ArrayList<>(); ChallengeLevels previousLevel = null; - for (Entry> en : challengeList.entrySet()) { - int challsToDo = 0; // TODO - calculate how many challenges still to do for this player - boolean complete = false; // TODO - result.add(new LevelStatus(en.getKey(), previousLevel, challsToDo, complete)); + // The first level is always unlocked + boolean isUnlocked = true; + // For each challenge level, check how many the user has done + for (Entry> en : challengeMap.entrySet()) { + int total = challengeMap.values().size(); + int waiverAmount = en.getKey().getWaiveramount(); + int challengesDone = (int) en.getValue().stream().filter(ch -> pd.isChallengeDone(ch.getUniqueId())).count(); + int challsToDo = Math.max(0,total-challengesDone-waiverAmount); + boolean complete = challsToDo > 0 ? false : true; + // Create result class with the data + result.add(new LevelStatus(en.getKey(), previousLevel, challsToDo, complete, isUnlocked)); + // Set up the next level for the next loop + previousLevel = en.getKey(); + isUnlocked = complete; } return result; } @@ -183,8 +238,8 @@ public class ChallengesManager { /** * @return the challengeList */ - public LinkedHashMap> getChallengeList() { - return challengeList; + public Map> getChallengeList() { + return challengeMap; } /** @@ -193,7 +248,8 @@ public class ChallengesManager { * @return the set of challenges for this level, or the first set of challenges if level is blank, or a blank list if there are no challenges */ public Set getChallenges(String level) { - return challengeList.getOrDefault(level, challengeList.isEmpty() ? new HashSet() : challengeList.values().iterator().next()); + Optional lv = challengeMap.keySet().stream().filter(l -> l.getUniqueId().equalsIgnoreCase(level)).findFirst(); + return lv.isPresent() ? challengeMap.get(lv.get()) : new HashSet<>(); } /** @@ -210,7 +266,7 @@ public class ChallengesManager { */ public ChallengeLevels getPreviousLevel(ChallengeLevels currentLevel) { ChallengeLevels result = null; - for (ChallengeLevels level : challengeList.keySet()) { + for (ChallengeLevels level : challengeMap.keySet()) { if (level.equals(currentLevel)) { return result; } @@ -225,8 +281,8 @@ public class ChallengesManager { * @return true if it exists, otherwise false */ public boolean isChallenge(String name) { - for (Set ch : challengeList.values()) { - if (ch.stream().filter(c -> c.getUniqueId().equalsIgnoreCase(name)).findFirst().isPresent()) { + for (Set ch : challengeMap.values()) { + if (ch.stream().anyMatch(c -> c.getUniqueId().equalsIgnoreCase(name))) { return true; } } @@ -236,30 +292,23 @@ public class ChallengesManager { /** * Checks if a challenge is complete or not * @param uniqueId - unique ID - player's UUID - * @param uniqueId2 - Challenge id + * @param challengeName - Challenge uniqueId * @return - true if completed */ - public boolean isChallengeComplete(User user, String uniqueId2) { - // TODO Auto-generated method stub - return false; + public boolean isChallengeComplete(User user, String challengeName) { + addPlayer(user); + return playerData.get(user.getUniqueId()).isChallengeDone(challengeName); } /** - * Checks number of challenges - * @return true if no challenges + * Check is user can see level + * @param user - user + * @param level - level unique id + * @return true if level is unlocked */ - public boolean isFirstTime() { - return challengeList.isEmpty(); - } - - public boolean isLevelAvailable(User user, String level) { - // TODO - return false; - } - - public boolean isLevelComplete(User user, ChallengeLevels otherLevel) { - // TODO Auto-generated method stub - return false; + public boolean isLevelUnlocked(User user, String level) { + addPlayer(user); + return getChallengeLevelStatus(user).stream().filter(LevelStatus::isUnlocked).anyMatch(lv -> lv.getLevel().getUniqueId().equalsIgnoreCase(level)); } /** @@ -267,20 +316,21 @@ public class ChallengesManager { */ public void load() { // Load the challenges - challengeList.clear(); - Bukkit.getLogger().info("Loading challenges..."); - for (Challenges challenge : chConfig.loadConfigObjects()) { - Bukkit.getLogger().info("Loading challenge " + challenge.getFriendlyName() + " level " + challenge.getLevel()); - storeChallenge(challenge); - } + challengeMap.clear(); + addon.getLogger().info("Loading challenges..."); + chConfig.loadConfigObjects().forEach(this::storeChallenge); sortChallenges(); } - + + /** + * Save configs and player data + */ private void save() { - challengeList.entrySet().forEach(en -> { + challengeMap.entrySet().forEach(en -> { lvConfig.saveConfigObject(en.getKey()); en.getValue().forEach(chConfig::saveConfigObject); }); + playerData.values().forEach(players :: saveObject); } /** @@ -289,7 +339,7 @@ public class ChallengesManager { */ public void save(boolean async) { if (async) { - BSkyBlock.getInstance().getServer().getScheduler().runTaskAsynchronously(BSkyBlock.getInstance(), () -> save()); + addon.getServer().getScheduler().runTaskAsynchronously(addon.getBSkyBlock(), this::save); } else { save(); } @@ -300,21 +350,21 @@ public class ChallengesManager { * @param user * @param uniqueId */ - public void setChallengeComplete(User user, String uniqueId) { - // TODO Auto-generated method stub - + public void setChallengeComplete(User user, String challengeUniqueId) { + addPlayer(user); + playerData.get(user.getUniqueId()).setChallengeDone(challengeUniqueId); } /** * @param challengeList the challengeList to set */ - public void setChallengeList(LinkedHashMap> challengeList) { - this.challengeList = challengeList; + public void setChallengeList(Map> challengeList) { + this.challengeMap = challengeList; } public void sortChallenges() { // Sort the challenge list into level order - challengeList = challengeList.entrySet().stream() + challengeMap = challengeMap.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)); @@ -328,30 +378,25 @@ public class ChallengesManager { // See if we have this level already ChallengeLevels level; if (lvConfig.configObjectExists(challenge.getLevel())) { - //Bukkit.getLogger().info("DEBUG: Level contains level " + challenge.getLevel()); // Get it from the database level = lvConfig.loadConfigObject(challenge.getLevel()); } else { - //Bukkit.getLogger().info("DEBUG: Level does not contains level " + challenge.getLevel()); // Make it level = new ChallengeLevels(); level.setUniqueId(challenge.getLevel()); - //Bukkit.getLogger().info("DEBUG: Level unique Id set to " + level.getUniqueId()); lvConfig.saveConfigObject(level); } - if (challengeList.containsKey(level)) { - //Bukkit.getLogger().info("DEBUG: Challenge contains level " + level.getUniqueId()); + if (challengeMap.containsKey(level)) { // Replace if this challenge uniqueId already exists - if (challengeList.get(level).contains(challenge)) { - challengeList.get(level).remove(challenge); + if (challengeMap.get(level).contains(challenge)) { + challengeMap.get(level).remove(challenge); } - challengeList.get(level).add(challenge); + challengeMap.get(level).add(challenge); } else { - //Bukkit.getLogger().info("DEBUG: No key found"); // First challenge of this level type Set challenges = new HashSet<>(); challenges.add(challenge); - challengeList.put(level, challenges); + challengeMap.put(level, challenges); } } @@ -363,4 +408,16 @@ public class ChallengesManager { lvConfig.saveConfigObject(level); } + /** + * Simple splitter + * @param string - string to be split + * @return list of split strings + */ + public List stringSplit(String string) { + string = ChatColor.translateAlternateColorCodes('&', string); + // Check length of lines + List result = new ArrayList<>(); + Arrays.asList(string.split("\\|")).forEach(line -> result.addAll(Arrays.asList(WordUtils.wrap(line,25).split("\\n")))); + return result; + } } diff --git a/src/main/java/bskyblock/addon/challenges/FreshSqueezedChallenges.java b/src/main/java/bskyblock/addon/challenges/FreshSqueezedChallenges.java index 89b5101..69f00cc 100644 --- a/src/main/java/bskyblock/addon/challenges/FreshSqueezedChallenges.java +++ b/src/main/java/bskyblock/addon/challenges/FreshSqueezedChallenges.java @@ -3,28 +3,23 @@ package bskyblock.addon.challenges; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; +import java.util.EnumMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.PotionMeta; -import org.bukkit.inventory.meta.SpawnEggMeta; -import org.bukkit.potion.PotionData; -import org.bukkit.potion.PotionType; import bskyblock.addon.challenges.database.object.ChallengeLevels; import bskyblock.addon.challenges.database.object.Challenges; public class FreshSqueezedChallenges { - private static final boolean DEBUG = false; ChallengesAddon addon; YamlConfiguration chal; @@ -83,8 +78,8 @@ public class FreshSqueezedChallenges { newChallenge.setUniqueId(challenge); ConfigurationSection details = chals.getConfigurationSection(challenge); newChallenge.setFriendlyName(details.getString("friendlyname", challenge)); - newChallenge.setDescription(details.getString("description", "")); - newChallenge.setIcon(parseItem(details.getString("icon") + ":1")); + newChallenge.setDescription(addon.getChallengesManager().stringSplit(details.getString("description", ""))); + newChallenge.setIcon(new ParseItem(addon, details.getString("icon") + ":1").getItem()); newChallenge.setLevel(details.getString("level", ChallengesManager.FREE)); newChallenge.setChallengeType(Challenges.ChallengeType.valueOf(details.getString("type","INVENTORY").toUpperCase())); newChallenge.setTakeItems(details.getBoolean("takeItems",true)); @@ -101,12 +96,13 @@ public class FreshSqueezedChallenges { // TODO reset allowed newChallenge.setReqMoney(details.getInt("requiredMoney")); newChallenge.setReqExp(details.getInt("requiredExp")); + String reqItems = details.getString("requiredItems",""); if (newChallenge.getChallengeType().equals(Challenges.ChallengeType.INVENTORY)) { - newChallenge.setRequiredItems(parseItems(details.getString("requiredItems",""))); + newChallenge.setRequiredItems(parseItems(reqItems)); } else if (newChallenge.getChallengeType().equals(Challenges.ChallengeType.LEVEL)) { - newChallenge.setReqIslandlevel(Long.parseLong(details.getString("requiredItems",""))); + newChallenge.setReqIslandlevel(Long.parseLong(reqItems)); } else if (newChallenge.getChallengeType().equals(Challenges.ChallengeType.ISLAND)) { - parseEntities(newChallenge, details.getString("requiredItems","")); + parseEntities(newChallenge, reqItems); } newChallenge.setItemReward(parseItems(details.getString("itemReward"))); newChallenge.setRepeatItemReward(parseItems(details.getString("repeatItemReward"))); @@ -116,26 +112,20 @@ public class FreshSqueezedChallenges { addon.getChallengesManager().sortChallenges(); } + /** + * Run through entity types and materials and try to match to the string given + * @param challenge - challenge to be adjusted + * @param string - string from YAML file + */ private void parseEntities(Challenges challenge, String string) { - Map req = new HashMap<>(); - Map blocks = new HashMap<>(); + Map req = new EnumMap<>(EntityType.class); + Map blocks = new EnumMap<>(Material.class); if (!string.isEmpty()) { for (String s : string.split(" ")) { String[] part = s.split(":"); try { - for (EntityType type : EntityType.values()) { - if (type.toString().equalsIgnoreCase(part[0])) { - req.put(type, Integer.valueOf(part[1])); - break; - } - } - for (Material type : Material.values()) { - if (type.toString().equalsIgnoreCase(part[0])) { - blocks.put(type, Integer.valueOf(part[1])); - break; - } - } - + Arrays.asList(EntityType.values()).stream().filter(t -> t.name().equalsIgnoreCase(part[0])).forEach(t -> req.put(t, Integer.valueOf(part[1]))); + Arrays.asList(Material.values()).stream().filter(t -> t.name().equalsIgnoreCase(part[0])).forEach(t -> blocks.put(t, Integer.valueOf(part[1]))); } catch (Exception e) { addon.getLogger().severe("Cannot parse '" + s + "'. Skipping..."); } @@ -149,7 +139,7 @@ public class FreshSqueezedChallenges { List result = new ArrayList<>(); if (!reqList.isEmpty()) { for (String s : reqList.split(" ")) { - ItemStack item = parseItem(s); + ItemStack item = new ParseItem(addon,s).getItem(); if (item != null) { result.add(item); } @@ -158,105 +148,6 @@ public class FreshSqueezedChallenges { return result; } - @SuppressWarnings("deprecation") - private ItemStack parseItem(String s) { - Material reqItem = null; - int reqAmount = 0; - String[] part = s.split(":"); - // Correct some common mistakes - if (part[0].equalsIgnoreCase("potato")) { - part[0] = "POTATO_ITEM"; - } else if (part[0].equalsIgnoreCase("brewing_stand")) { - part[0] = "BREWING_STAND_ITEM"; - } else if (part[0].equalsIgnoreCase("carrot")) { - part[0] = "CARROT_ITEM"; - } else if (part[0].equalsIgnoreCase("cauldron")) { - part[0] = "CAULDRON_ITEM"; - } else if (part[0].equalsIgnoreCase("skull")) { - part[0] = "SKULL_ITEM"; - } - // TODO: add netherwart vs. netherstalk? - // Material:Qty - if (part.length == 2) { - try { - if (StringUtils.isNumeric(part[0])) { - reqItem = Material.getMaterial(Integer.parseInt(part[0])); - } else { - reqItem = Material.getMaterial(part[0].toUpperCase()); - } - reqAmount = Integer.parseInt(part[1]); - ItemStack item = new ItemStack(reqItem); - if (DEBUG) { - addon.getLogger().info("DEBUG: required item = " + reqItem.toString()); - addon.getLogger().info("DEBUG: item amount = " + reqAmount); - } - return item; - } catch (Exception e) { - addon.getLogger().severe("Problem with " + s + " in challenges.yml!"); - } - } else if (part.length == 3) { - if (DEBUG) - addon.getLogger().info("DEBUG: Item with durability"); - if (StringUtils.isNumeric(part[0])) { - reqItem = Material.getMaterial(Integer.parseInt(part[0])); - } else { - reqItem = Material.getMaterial(part[0].toUpperCase()); - } - reqAmount = Integer.parseInt(part[2]); - ItemStack item = new ItemStack(reqItem); - int reqDurability = 0; - if (StringUtils.isNumeric(part[1])) { - reqDurability = Integer.parseInt(part[1]); - item.setDurability((short) reqDurability); - } else if (reqItem.equals(Material.MONSTER_EGG)) { - reqDurability = -1; // non existent - // Check if this is a string - EntityType entityType = EntityType.valueOf(part[1]); - item = new ItemStack(Material.MONSTER_EGG); - SpawnEggMeta meta = ((SpawnEggMeta)item.getItemMeta()); - meta.setSpawnedType(entityType); - item.setItemMeta(meta); - } - return item; - } else if (part.length == 6 && part[0].contains("POTION")) { - try { - reqAmount = Integer.parseInt(part[5]); - if (DEBUG) - addon.getLogger().info("DEBUG: required amount is " + reqAmount); - } catch (Exception e) { - addon.getLogger().severe("Could not parse the quantity of the potion item " + s); - return null; - } - /* - * # Format POTION:NAME::::QTY - # LEVEL, EXTENDED, SPLASH, LINGER are optional. - # LEVEL is a number, 1 or 2 - # LINGER is for V1.9 servers and later - # Examples: - # POTION:STRENGTH:1:EXTENDED:SPLASH:1 - # POTION:INSTANT_DAMAGE:2::LINGER:2 - # POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1 - # POTION:WEAKNESS::::1 - any weakness potion - */ - ItemStack item = part[4].isEmpty() ? new ItemStack(Material.POTION) : part[4].equalsIgnoreCase("SPLASH") - ? new ItemStack(Material.SPLASH_POTION) : new ItemStack(Material.LINGERING_POTION); - PotionMeta potionMeta = (PotionMeta)(item.getItemMeta()); - PotionType type = PotionType.valueOf(part[1].toUpperCase()); - boolean isExtended = part[3].equalsIgnoreCase("EXTENDED") ? true : false; - boolean isUpgraded = (part[4].isEmpty() || part[4].equalsIgnoreCase("1")) ? false: true; - PotionData data = new PotionData(type, isExtended, isUpgraded); - potionMeta.setBasePotionData(data); - - item.setAmount(reqAmount); - return item; - - } else { - addon.getLogger().severe("Problem with " + s + " in challenges.yml!"); - } - - return null; - - } } diff --git a/src/main/java/bskyblock/addon/challenges/LevelStatus.java b/src/main/java/bskyblock/addon/challenges/LevelStatus.java index 9427ee9..9dffe03 100644 --- a/src/main/java/bskyblock/addon/challenges/LevelStatus.java +++ b/src/main/java/bskyblock/addon/challenges/LevelStatus.java @@ -12,13 +12,22 @@ public class LevelStatus { private final ChallengeLevels previousLevel; private final int numberOfChallengesStillToDo; private final boolean complete; + private final boolean isUnlocked; - public LevelStatus(ChallengeLevels level, ChallengeLevels previousLevel, int numberOfChallengesStillToDo, boolean complete) { + /** + * @param level - level + * @param previousLevel - previous level + * @param numberOfChallengesStillToDo - number of challenges still to do on this level + * @param complete - whether complete or not + * @param isUnlocked + */ + public LevelStatus(ChallengeLevels level, ChallengeLevels previousLevel, int numberOfChallengesStillToDo, boolean complete, boolean isUnlocked) { super(); this.level = level; this.previousLevel = previousLevel; this.numberOfChallengesStillToDo = numberOfChallengesStillToDo; this.complete = complete; + this.isUnlocked = isUnlocked; } /** * @return the level @@ -44,6 +53,12 @@ public class LevelStatus { public boolean isComplete() { return complete; } + /** + * @return the isUnlocked + */ + public boolean isUnlocked() { + return isUnlocked; + } } diff --git a/src/main/java/bskyblock/addon/challenges/ParseItem.java b/src/main/java/bskyblock/addon/challenges/ParseItem.java new file mode 100644 index 0000000..fcc31cc --- /dev/null +++ b/src/main/java/bskyblock/addon/challenges/ParseItem.java @@ -0,0 +1,140 @@ +package bskyblock.addon.challenges; + +import org.apache.commons.lang.StringUtils; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.potion.PotionData; +import org.bukkit.potion.PotionType; + +/** + * Class that parses a string into an ItemStack + * Used for converting config file entries to objects + * @author tastybento + * + */ +public class ParseItem { + + private final ItemStack item; + private ChallengesAddon addon; + + public ParseItem(ChallengesAddon addon, String s) { + this.addon = addon; + item = parseItem(s); + } + + /** + * Parse a string into an itemstack + * @param s - input string + * @return ItemStack or null if parsing failed + */ + private ItemStack parseItem(String s) { + String[] part = s.split(":"); + // Material:Qty + if (part.length == 2) { + return two(s, part); + } else if (part.length == 3) { + return three(s, part); + } else if (part.length == 6 && (part[0].contains("POTION") || part[0].equalsIgnoreCase("TIPPED_ARROW"))) { + return potion(s, part); + } + return null; + + } + + private ItemStack potion(String s, String[] part) { + int reqAmount = 0; + try { + reqAmount = Integer.parseInt(part[5]); + } catch (Exception e) { + addon.getLogger().severe(() -> "Could not parse the quantity of the potion or tipped arrow " + s); + return null; + } + /* + * # Format POTION:NAME::::QTY + # LEVEL, EXTENDED, SPLASH, LINGER are optional. + # LEVEL is a number, 1 or 2 + # LINGER is for V1.9 servers and later + # Examples: + # POTION:STRENGTH:1:EXTENDED:SPLASH:1 + # POTION:INSTANT_DAMAGE:2::LINGER:2 + # POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1 + # POTION:WEAKNESS::::1 - any weakness potion + */ + ItemStack result = new ItemStack(Material.POTION); + if (part[4].equalsIgnoreCase("SPLASH")) { + result = new ItemStack(Material.SPLASH_POTION); + } else if (part[4].equalsIgnoreCase("LINGER")) { + result = new ItemStack(Material.LINGERING_POTION); + } + if (part[0].equalsIgnoreCase("TIPPED_ARROW")) { + result = new ItemStack(Material.TIPPED_ARROW); + } + result.setAmount(reqAmount); + PotionMeta potionMeta = (PotionMeta)(result.getItemMeta()); + PotionType type = PotionType.valueOf(part[1].toUpperCase()); + boolean isUpgraded = (part[2].isEmpty() || part[2].equalsIgnoreCase("1")) ? false: true; + boolean isExtended = part[3].equalsIgnoreCase("EXTENDED") ? true : false; + PotionData data = new PotionData(type, isExtended, isUpgraded); + potionMeta.setBasePotionData(data); + + result.setAmount(reqAmount); + return result; + } + + private ItemStack three(String s, String[] part) { + // Rearrange + String[] twoer = {part[0], part[2]}; + ItemStack result = two(s, twoer); + if (result == null) { + return null; + } + if (StringUtils.isNumeric(part[1])) { + result.setDurability((short) Integer.parseInt(part[1])); + } else if (result.getType().equals(Material.MONSTER_EGG)) { + // Check if this is a string + EntityType entityType = EntityType.valueOf(part[1]); + SpawnEggMeta meta = ((SpawnEggMeta)result.getItemMeta()); + meta.setSpawnedType(entityType); + result.setItemMeta(meta); + } + return result; + + } + + private void showError(String s) { + addon.getLogger().severe(() -> "Problem with " + s + " in challenges.yml!"); + } + + private ItemStack two(String s, String[] part) { + int reqAmount = 0; + try { + reqAmount = Integer.parseInt(part[1]); + } catch (Exception e) { + addon.getLogger().severe(() -> "Could not parse the quantity of the item " + s); + return null; + } + + Material reqItem = Material.getMaterial(part[0].toUpperCase() + "_ITEM"); + if (reqItem == null) { + // Try the item + reqItem = Material.getMaterial(part[0].toUpperCase()); + } + + if (reqItem == null) { + showError(s); + return null; + } + return new ItemStack(reqItem, reqAmount); + + } + + /** + * @return the item + */ + public ItemStack getItem() { + return item; + } +} diff --git a/src/main/java/bskyblock/addon/challenges/commands/ChallengesCommand.java b/src/main/java/bskyblock/addon/challenges/commands/ChallengesCommand.java index e5f0bd7..ba6c342 100644 --- a/src/main/java/bskyblock/addon/challenges/commands/ChallengesCommand.java +++ b/src/main/java/bskyblock/addon/challenges/commands/ChallengesCommand.java @@ -8,7 +8,7 @@ import us.tastybento.bskyblock.api.commands.CompositeCommand; import us.tastybento.bskyblock.api.user.User; public class ChallengesCommand extends CompositeCommand { - private static final String CHALLENGE_COMMAND = "challenges"; + public static final String CHALLENGE_COMMAND = "challenges"; private ChallengesAddon addon; public ChallengesCommand(ChallengesAddon addon) { @@ -21,7 +21,7 @@ public class ChallengesCommand extends CompositeCommand { public boolean execute(User user, List args) { // Open up the challenges GUI if (user.isPlayer()) { - addon.getChallengesManager().getChallengesPanels().getChallenges(user); + addon.getChallengesManager().getChallengesPanels().getChallenges(user, args.isEmpty() ? "" : args.get(0)); return true; } return false; @@ -30,9 +30,9 @@ public class ChallengesCommand extends CompositeCommand { @Override public void setup() { this.setOnlyPlayer(true); - this.setPermission(Constants.PERMPREFIX + "challenges"); - this.setParameters("challaneges.parameters"); - this.setDescription("challenges.description"); + this.setPermission(Constants.PERMPREFIX + CHALLENGE_COMMAND); + this.setParameters(CHALLENGE_COMMAND + ".parameters"); + this.setDescription(CHALLENGE_COMMAND + ".description"); this.setOnlyPlayer(true); } diff --git a/src/main/java/bskyblock/addon/challenges/commands/admin/CreateSurrounding.java b/src/main/java/bskyblock/addon/challenges/commands/admin/CreateSurrounding.java index adc6b54..c52c18d 100644 --- a/src/main/java/bskyblock/addon/challenges/commands/admin/CreateSurrounding.java +++ b/src/main/java/bskyblock/addon/challenges/commands/admin/CreateSurrounding.java @@ -75,15 +75,14 @@ public class CreateSurrounding extends CompositeCommand implements Listener { @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public boolean onPlayerInteract(PlayerInteractEvent e) { - if (e.getAction().equals(Action.LEFT_CLICK_BLOCK)) { - if (inProgress.containsKey(e.getPlayer().getUniqueId())) { - // Prevent damage - e.setCancelled(true); - inProgress.get(e.getPlayer().getUniqueId()).addBlock(e.getClickedBlock().getType()); - User.getInstance(e.getPlayer()).sendMessage("challenges.admin.you-added", "[thing]", Util.prettifyText(e.getClickedBlock().getType().toString())); - return true; - } + if (e.getAction().equals(Action.LEFT_CLICK_BLOCK) && inProgress.containsKey(e.getPlayer().getUniqueId())) { + // Prevent damage + e.setCancelled(true); + inProgress.get(e.getPlayer().getUniqueId()).addBlock(e.getClickedBlock().getType()); + User.getInstance(e.getPlayer()).sendMessage("challenges.admin.you-added", "[thing]", Util.prettifyText(e.getClickedBlock().getType().toString())); + return true; } + if (e.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { return finished(e, e.getPlayer().getUniqueId()); } @@ -107,7 +106,7 @@ public class CreateSurrounding extends CompositeCommand implements Listener { public boolean onPlayerInteract(PlayerInteractAtEntityEvent e) { return finished(e, e.getPlayer().getUniqueId()); } - + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public boolean onLeft(EntityDamageByEntityEvent e) { if (!(e.getDamager() instanceof Player)) { @@ -123,6 +122,6 @@ public class CreateSurrounding extends CompositeCommand implements Listener { } return false; } - + } diff --git a/src/main/java/bskyblock/addon/challenges/commands/admin/SetIcon.java b/src/main/java/bskyblock/addon/challenges/commands/admin/SetIcon.java index 91c0b51..d3430e1 100644 --- a/src/main/java/bskyblock/addon/challenges/commands/admin/SetIcon.java +++ b/src/main/java/bskyblock/addon/challenges/commands/admin/SetIcon.java @@ -47,13 +47,13 @@ public class SetIcon extends CompositeCommand { ItemStack icon = user.getInventory().getItemInMainHand(); if (args.isEmpty() || icon == null) { user.sendMessage("challenges.admin.seticon.description"); - return true; + return false; } Challenges challenge = addon.getChallengesManager().getChallenge(args.get(0)); // Check if this challenge name exists if (challenge == null) { user.sendMessage("challenges.admin.seticon.error.no-such-challenge"); - return true; + return false; } challenge.setIcon(icon); user.sendMessage("general.success"); diff --git a/src/main/java/bskyblock/addon/challenges/commands/admin/SurroundChallengeBuilder.java b/src/main/java/bskyblock/addon/challenges/commands/admin/SurroundChallengeBuilder.java index af02ac5..d00af6d 100644 --- a/src/main/java/bskyblock/addon/challenges/commands/admin/SurroundChallengeBuilder.java +++ b/src/main/java/bskyblock/addon/challenges/commands/admin/SurroundChallengeBuilder.java @@ -1,6 +1,6 @@ package bskyblock.addon.challenges.commands.admin; -import java.util.HashMap; +import java.util.EnumMap; import java.util.Map; import org.bukkit.Material; @@ -18,8 +18,8 @@ public class SurroundChallengeBuilder { private ChallengesAddon addon; private String name; private User owner; - private Map reqBlocks = new HashMap<>(); - private Map reqEntities = new HashMap<>(); + private Map reqBlocks = new EnumMap<>(Material.class); + private Map reqEntities = new EnumMap<>(EntityType.class); public SurroundChallengeBuilder(ChallengesAddon addon) { this.addon = addon; diff --git a/src/main/java/bskyblock/addon/challenges/config/PluginConfig.java b/src/main/java/bskyblock/addon/challenges/config/PluginConfig.java deleted file mode 100644 index c684ed1..0000000 --- a/src/main/java/bskyblock/addon/challenges/config/PluginConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package bskyblock.addon.challenges.config; - -import bskyblock.addon.challenges.ChallengesAddon; - -public class PluginConfig { - - /** - * Loads the various settings from the config.yml file into the plugin - */ - public PluginConfig(ChallengesAddon plugin) { - plugin.saveDefaultConfig(); - - // Settings - Settings.resetChallenges = plugin.getConfig().getBoolean("resetchallenges"); - // Challenge completion broadcast - Settings.broadcastMessages = plugin.getConfig().getBoolean("broadcastmessages", true); - // Challenges - show or remove completed one-time challenges - Settings.removeCompleteOnetimeChallenges = plugin.getConfig().getBoolean("removecompleteonetimechallenges"); - // Add glow to completed challenge icons or not - Settings.addCompletedGlow = plugin.getConfig().getBoolean("addcompletedglow", true); - - // All done - } -} diff --git a/src/main/java/bskyblock/addon/challenges/config/Settings.java b/src/main/java/bskyblock/addon/challenges/config/Settings.java deleted file mode 100644 index b55f4d9..0000000 --- a/src/main/java/bskyblock/addon/challenges/config/Settings.java +++ /dev/null @@ -1,28 +0,0 @@ -package bskyblock.addon.challenges.config; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -public class Settings { - - // Storage - // Challenge List - public static Set challengeList; - // Waiver amount - public static int waiverAmount; - // List of challenge levels - public static List challengeLevels; - // Free levels - public static List freeLevels = new ArrayList(); - - // Settings - public static boolean resetChallenges; - // Challenge completion broadcast - public static boolean broadcastMessages; - // Challenges - show or remove completed on-time challenges - public static boolean removeCompleteOnetimeChallenges; - // Add glow to completed challenge icons or not - public static boolean addCompletedGlow; - -} diff --git a/src/main/java/bskyblock/addon/challenges/database/object/ChallengeLevels.java b/src/main/java/bskyblock/addon/challenges/database/object/ChallengeLevels.java index 59b1481..7656bac 100644 --- a/src/main/java/bskyblock/addon/challenges/database/object/ChallengeLevels.java +++ b/src/main/java/bskyblock/addon/challenges/database/object/ChallengeLevels.java @@ -60,6 +60,10 @@ public class ChallengeLevels implements DataObject, Comparable return uniqueId; } + /** + * Get the number of undone tasks that can be left on a level before unlocking next level + * @return + */ public int getWaiveramount() { return waiveramount; } @@ -93,43 +97,7 @@ public class ChallengeLevels implements DataObject, Comparable public int compareTo(ChallengeLevels o) { return Integer.compare(this.order, o.order); } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof ChallengeLevels)) { - return false; - } - ChallengeLevels other = (ChallengeLevels) obj; - if (uniqueId == null) { - if (other.uniqueId != null) { - return false; - } - } else if (!uniqueId.equals(other.uniqueId)) { - return false; - } - return true; - } - + /** * @return the rewardDescription */ @@ -199,5 +167,41 @@ public class ChallengeLevels implements DataObject, Comparable public void setUnlockMessage(String unlockMessage) { this.unlockMessage = unlockMessage; } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ChallengeLevels)) { + return false; + } + ChallengeLevels other = (ChallengeLevels) obj; + if (uniqueId == null) { + if (other.uniqueId != null) { + return false; + } + } else if (!uniqueId.equals(other.uniqueId)) { + return false; + } + return true; + } } diff --git a/src/main/java/bskyblock/addon/challenges/database/object/Challenges.java b/src/main/java/bskyblock/addon/challenges/database/object/Challenges.java index 5b7bdea..4447670 100644 --- a/src/main/java/bskyblock/addon/challenges/database/object/Challenges.java +++ b/src/main/java/bskyblock/addon/challenges/database/object/Challenges.java @@ -1,14 +1,12 @@ package bskyblock.addon.challenges.database.object; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; +import java.util.EnumMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.entity.EntityType; @@ -20,42 +18,6 @@ import us.tastybento.bskyblock.database.objects.DataObject; public class Challenges implements DataObject { - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Challenges)) { - return false; - } - Challenges other = (Challenges) obj; - if (uniqueId == null) { - if (other.uniqueId != null) { - return false; - } - } else if (!uniqueId.equals(other.uniqueId)) { - return false; - } - return true; - } - public enum ChallengeType { /** * This challenge only shows and icon in the GUI and doesn't do anything. @@ -109,11 +71,11 @@ public class Challenges implements DataObject { // Requirements @ConfigComment("This is a map of the blocks required in a SURROUNDING challenge. Material, Integer") - private Map requiredBlocks = new HashMap<>(); + private Map requiredBlocks = new EnumMap<>(Material.class); @ConfigComment("The items that must be in the inventory to complete the challenge. ItemStack List.") private List requiredItems = new ArrayList<>(); @ConfigComment("Any entities that must be in the area for SURROUNDING type challenges. Map EntityType, Number") - private Map requiredEntities = new HashMap<>(); + private Map requiredEntities = new EnumMap<>(EntityType.class); @ConfigComment("Required experience") private int reqExp; @ConfigComment("Required island level for this challenge. Only works if Level Addon is being used.") @@ -613,14 +575,39 @@ public class Challenges implements DataObject { this.uniqueId = uniqueId; } - /** - * Create a description from a single string - * Use | as new line, & as a color char - * @param string + /* (non-Javadoc) + * @see java.lang.Object#hashCode() */ - public void setDescription(String string) { - string = ChatColor.translateAlternateColorCodes('&', string); - this.description = Arrays.asList(string.split("\\|")); + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; } + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Challenges)) { + return false; + } + Challenges other = (Challenges) obj; + if (uniqueId == null) { + if (other.uniqueId != null) { + return false; + } + } else if (!uniqueId.equals(other.uniqueId)) { + return false; + } + return true; + } } diff --git a/src/main/java/bskyblock/addon/challenges/database/object/PlayerData.java b/src/main/java/bskyblock/addon/challenges/database/object/PlayerData.java new file mode 100644 index 0000000..f2a0c88 --- /dev/null +++ b/src/main/java/bskyblock/addon/challenges/database/object/PlayerData.java @@ -0,0 +1,159 @@ +/** + * + */ +package bskyblock.addon.challenges.database.object; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import us.tastybento.bskyblock.database.objects.DataObject; + +/** + * Stores the player's challenge situation + * @author tastybento + * + */ +public class PlayerData implements DataObject { + + private String uniqueId = ""; + /** + * Challenge map, where key = unique challenge name and Value = number of times completed + */ + private Map challengeStatus = new HashMap<>(); + private Map challengesTimestamp = new HashMap<>(); + private Set levelsDone = new HashSet<>(); + + // Required for bean instantiation + public PlayerData() {} + + /** + * Mark a challenge as having been completed. Will increment the number of times and timestamp + * @param challengeName - unique challenge name + */ + public void setChallengeDone(String challengeName) { + int times = challengeStatus.getOrDefault(challengeName, 0) + 1; + challengeStatus.put(challengeName, times); + challengesTimestamp.put(challengeName, System.currentTimeMillis()); + } + + /** + * Check if a challenge has been done + * @param challengeName - unique challenge name + * @return true if done at least once + */ + public boolean isChallengeDone(String challengeName) { + return getTimes(challengeName) > 0 ? true : false; + } + + /** + * Check how many times a challenge has been done + * @param challengeName - unique challenge name + * @return - number of times + */ + public int getTimes(String challengeName) { + return challengeStatus.getOrDefault(challengeName, 0); + } + + /** + * Creates a player data entry + * @param uniqueId - the player's UUID in string format + */ + public PlayerData(String uniqueId) { + this.uniqueId = uniqueId; + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.objects.DataObject#getUniqueId() + */ + @Override + public String getUniqueId() { + // TODO Auto-generated method stub + return uniqueId; + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.objects.DataObject#setUniqueId(java.lang.String) + */ + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + + } + + /** + * @return the challengeStatus + */ + public Map getChallengeStatus() { + return challengeStatus; + } + /** + * @param challengeStatus the challengeStatus to set + */ + public void setChallengeStatus(Map challengeStatus) { + this.challengeStatus = challengeStatus; + } + /** + * @return the challengesTimestamp + */ + public Map getChallengesTimestamp() { + return challengesTimestamp; + } + /** + * @param challengesTimestamp the challengesTimestamp to set + */ + public void setChallengesTimestamp(Map challengesTimestamp) { + this.challengesTimestamp = challengesTimestamp; + } + /** + * @return the levelsDone + */ + public Set getLevelsDone() { + return levelsDone; + } + + /** + * @param levelsDone the levelsDone to set + */ + public void setLevelsDone(Set levelsDone) { + this.levelsDone = levelsDone; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof PlayerData)) { + return false; + } + PlayerData other = (PlayerData) obj; + if (uniqueId == null) { + if (other.uniqueId != null) { + return false; + } + } else if (!uniqueId.equals(other.uniqueId)) { + return false; + } + return true; + } + +} diff --git a/src/main/java/bskyblock/addon/challenges/panel/ChallengesPanels.java b/src/main/java/bskyblock/addon/challenges/panel/ChallengesPanels.java index 5a37b2a..34b53be 100644 --- a/src/main/java/bskyblock/addon/challenges/panel/ChallengesPanels.java +++ b/src/main/java/bskyblock/addon/challenges/panel/ChallengesPanels.java @@ -1,18 +1,16 @@ package bskyblock.addon.challenges.panel; -import java.util.Arrays; import java.util.Set; -import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import bskyblock.addon.challenges.ChallengesAddon; import bskyblock.addon.challenges.ChallengesManager; import bskyblock.addon.challenges.LevelStatus; +import bskyblock.addon.challenges.commands.ChallengesCommand; import bskyblock.addon.challenges.database.object.Challenges; import bskyblock.addon.challenges.database.object.Challenges.ChallengeType; -import us.tastybento.bskyblock.api.panels.ClickType; import us.tastybento.bskyblock.api.panels.Panel; import us.tastybento.bskyblock.api.panels.PanelItem; import us.tastybento.bskyblock.api.panels.builders.PanelBuilder; @@ -21,7 +19,6 @@ import us.tastybento.bskyblock.api.user.User; public class ChallengesPanels { - private static final boolean DEBUG = true; private ChallengesAddon addon; private ChallengesManager manager; @@ -44,107 +41,105 @@ public class ChallengesPanels { * level */ public void getChallenges(User user, String level) { - addon.getLogger().info("DEBUG: level requested = " + level); + if (manager.getChallengeList().isEmpty()) { + addon.getLogger().severe("There are no challenges set up!"); + user.sendMessage("general.errors.general"); + return; + } + if (level.isEmpty()) { + level = manager.getChallengeList().keySet().iterator().next().getUniqueId(); + } + // Check if level is valid + if (!manager.isLevelUnlocked(user, level)) { + return; + } PanelBuilder panelBuilder = new PanelBuilder() - .name(user.getTranslation("challenges.guiTitle")); + .name(user.getTranslation("challenges.gui-title")); addChallengeItems(panelBuilder, user, level); - addFreeChallanges(panelBuilder); - addNavigation(panelBuilder, user); + addNavigation(panelBuilder, user, level); + addFreeChallanges(panelBuilder, user); // Create the panel - addon.getLogger().info("DEBUG: panel created"); Panel panel = panelBuilder.build(); panel.open(user); } - private void addFreeChallanges(PanelBuilder panelBuilder) { - /* - // Add the free challenges if not already shown (which can happen if all of the challenges are done!) - if (!level.equals("") && challengeList.containsKey("")) { - for (String freeChallenges: challengeList.get("")) { - CPItem item = createItem(freeChallenges, player); - if (item != null) { - cp.add(item); - } - } - }*/ + private void addFreeChallanges(PanelBuilder panelBuilder, User user) { + manager.getChallenges(ChallengesManager.FREE).forEach(challenge -> createItem(panelBuilder, challenge, user)); + } + + /** + * Creates a panel item for challenge if appropriate and adds it to panelBuilder + * @param panelBuilder + * @param challenge + * @param user + */ + private void createItem(PanelBuilder panelBuilder, Challenges challenge, User user) { + // Check completion + boolean completed = manager.isChallengeComplete(user, challenge.getUniqueId()); + // If challenge is removed after completion, remove it + if (completed && challenge.isRemoveWhenCompleted()) { + return; + } + PanelItem item = new PanelItemBuilder() + .icon(challenge.getIcon()) + .name(challenge.getFriendlyName().isEmpty() ? challenge.getUniqueId() : challenge.getFriendlyName()) + .description(challenge.getDescription()) + .glow(completed) + .clickHandler((player,c) -> { + if (!challenge.getChallengeType().equals(ChallengeType.ICON)) { + new TryToComplete(addon, player, manager, challenge); + } + return true; + }) + .build(); + if (challenge.getSlot() >= 0) { + panelBuilder.item(challenge.getSlot(),item); + } else { + panelBuilder.item(item); + } } private void addChallengeItems(PanelBuilder panelBuilder, User user, String level) { - Set levelChallenges = manager.getChallenges(level); - // Do some checking - if (DEBUG) - addon.getLogger().info("DEBUG: Opening level " + level + " with " + levelChallenges.size() + " challenges"); - // Only show a control panel for the level requested. for (Challenges challenge : levelChallenges) { - addon.getLogger().info("DEBUG: Adding challenge " + challenge.getUniqueId()); - // Check completion - boolean completed = manager.isChallengeComplete(user, challenge.getUniqueId()); - addon.getLogger().info("DEBUG: challenge completed = " + completed); - // If challenge is removed after completion, remove it - if (completed && challenge.isRemoveWhenCompleted()) { - addon.getLogger().info("DEBUG: ignored completed"); - continue; - } - PanelItem item = new PanelItemBuilder() - .icon(challenge.getIcon()) - .name(challenge.getFriendlyName().isEmpty() ? challenge.getUniqueId() : challenge.getFriendlyName()) - .description(challenge.getDescription()) - .glow(completed) - .clickHandler(new PanelItem.ClickHandler() { - @Override - public boolean onClick(User user, ClickType click) { - if (!challenge.getChallengeType().equals(ChallengeType.ICON)) { - new TryToComplete(addon, user, manager, challenge); - } - return true; - } - }) - .build(); - addon.getLogger().info("requested slot" + challenge.getSlot()); - if (challenge.getSlot() >= 0) { - panelBuilder.item(challenge.getSlot(),item); - } else { - panelBuilder.item(item); - } + createItem(panelBuilder, challenge, user); } - - } - private void addNavigation(PanelBuilder panelBuilder, User user) { - // TODO Auto-generated method stub + private void addNavigation(PanelBuilder panelBuilder, User user, String level) { // Add navigation to other levels for (LevelStatus status: manager.getChallengeLevelStatus(user)) { - String name = ChatColor.GOLD + (status.getLevel().getFriendlyName().isEmpty() ? status.getLevel().getUniqueId() : status.getLevel().getFriendlyName()); - if (status.isComplete() || status.getPreviousLevel() == null) { + if (status.getLevel().getUniqueId().equals(level)) { + // Skip if this is the current level + continue; + } + // Create a nice name for the level + String name = status.getLevel().getFriendlyName().isEmpty() ? status.getLevel().getUniqueId() : status.getLevel().getFriendlyName(); + + if (status.isUnlocked()) { // Clicking on this icon will open up this level's challenges PanelItem item = new PanelItemBuilder() .icon(new ItemStack(Material.BOOK_AND_QUILL)) .name(name) - .description(Arrays.asList(user.getTranslation("challenges.navigation","[level]",name))) - .clickHandler(new PanelItem.ClickHandler() { - - @Override - public boolean onClick(User user, ClickType click) { - // TODO Auto-generated method stub - return false; - } + .description(manager.stringSplit(user.getTranslation("challenges.navigation","[level]",name))) + .clickHandler((u, c) -> { + u.closeInventory(); + u.performCommand(ChallengesCommand.CHALLENGE_COMMAND + " " + status.getLevel().getUniqueId()); + return true; }) - //.setCommand(CHALLENGE_COMMAND + " c " + status.getLevel().getUniqueId()) .build(); panelBuilder.item(item); } else { // Clicking on this icon will do nothing because the challenge is not unlocked yet - String previousLevelName = ChatColor.GOLD + (status.getPreviousLevel().getFriendlyName().isEmpty() ? status.getPreviousLevel().getUniqueId() : status.getPreviousLevel().getFriendlyName()); + String previousLevelName = status.getPreviousLevel().getFriendlyName().isEmpty() ? status.getPreviousLevel().getUniqueId() : status.getPreviousLevel().getFriendlyName(); PanelItem item = new PanelItemBuilder() .icon(new ItemStack(Material.BOOK)) .name(name) - .description(Arrays.asList(user.getTranslation("challenges.toComplete", "[challengesToDo]",String.valueOf(status.getNumberOfChallengesStillToDo()), "[thisLevel]", previousLevelName))) + .description(manager.stringSplit(user.getTranslation("challenges.to-complete", "[challengesToDo]",String.valueOf(status.getNumberOfChallengesStillToDo()), "[thisLevel]", previousLevelName))) .build(); panelBuilder.item(item); } diff --git a/src/main/java/bskyblock/addon/challenges/panel/CreateChallengeListener.java b/src/main/java/bskyblock/addon/challenges/panel/CreateChallengeListener.java index b84e68a..220fb4a 100644 --- a/src/main/java/bskyblock/addon/challenges/panel/CreateChallengeListener.java +++ b/src/main/java/bskyblock/addon/challenges/panel/CreateChallengeListener.java @@ -18,7 +18,9 @@ public class CreateChallengeListener implements PanelListener { } @Override - public void setup() {} + public void setup() { + // Nothing to setup + } @Override public void onInventoryClose(InventoryCloseEvent event) { diff --git a/src/main/java/bskyblock/addon/challenges/panel/TryToComplete.java b/src/main/java/bskyblock/addon/challenges/panel/TryToComplete.java index cd6837e..d594dd9 100644 --- a/src/main/java/bskyblock/addon/challenges/panel/TryToComplete.java +++ b/src/main/java/bskyblock/addon/challenges/panel/TryToComplete.java @@ -4,11 +4,10 @@ package bskyblock.addon.challenges.panel; import java.util.ArrayList; -import java.util.HashMap; +import java.util.EnumMap; import java.util.List; import java.util.Map; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; @@ -16,6 +15,7 @@ import org.bukkit.util.Vector; import bskyblock.addon.challenges.ChallengesAddon; import bskyblock.addon.challenges.ChallengesManager; +import bskyblock.addon.challenges.commands.ChallengesCommand; import bskyblock.addon.challenges.database.object.Challenges; import bskyblock.addon.challenges.database.object.Challenges.ChallengeType; import bskyblock.addon.level.Level; @@ -32,22 +32,24 @@ public class TryToComplete { private ChallengesAddon addon; + /** + * @param addon + * @param user + * @param manager + * @param challenge + */ public TryToComplete(ChallengesAddon addon, User user, ChallengesManager manager, Challenges challenge) { this.addon = addon; - Bukkit.getLogger().info("DEBUG: try to complete"); // Check if user is in the worlds if (!Util.inWorld(user.getLocation())) { user.sendMessage("general.errors.wrong-world"); return; } - Bukkit.getLogger().info("DEBUG: right world"); // Check if can complete challenge ChallengeResult result = checkIfCanCompleteChallenge(user, manager, challenge); if (!result.meetsRequirements) { - Bukkit.getLogger().info("DEBUG: could not complete"); return; } - Bukkit.getLogger().info("DEBUG: Can complete!"); if (!result.repeat) { // Give rewards for (ItemStack reward : challenge.getItemReward()) { @@ -73,8 +75,10 @@ public class TryToComplete { runCommands(user, challenge.getRepeatRewardCommands()); user.sendMessage("challenges.you-repeated", "[challenge]", challenge.getFriendlyName()); } - // Market as complete + // Mark as complete manager.setChallengeComplete(user, challenge.getUniqueId()); + user.closeInventory(); + user.getPlayer().performCommand(ChallengesCommand.CHALLENGE_COMMAND + " " + challenge.getLevel()); } /** @@ -82,47 +86,40 @@ public class TryToComplete { */ private ChallengeResult checkIfCanCompleteChallenge(User user, ChallengesManager manager, Challenges challenge) { // Check if user has the - if (!challenge.getLevel().equals(ChallengesManager.FREE) && !manager.isLevelAvailable(user, challenge.getLevel())) { + if (!challenge.getLevel().equals(ChallengesManager.FREE) && !manager.isLevelUnlocked(user, challenge.getLevel())) { user.sendMessage("challenges.errors.challenge-level-not-available"); return new ChallengeResult(); } - Bukkit.getLogger().info("DEBUG: Level is available or challenge is free"); // Check max times - if (challenge.isRepeatable() && challenge.getMaxTimes() > 0) { - Bukkit.getLogger().info("DEBUG: repeatable and max times > 0"); - if (manager.checkChallengeTimes(user, challenge) >= challenge.getMaxTimes()) { - user.sendMessage("challenges.errors.cannot-repeat"); - return new ChallengeResult(); - } + if (challenge.isRepeatable() && challenge.getMaxTimes() > 0 && manager.checkChallengeTimes(user, challenge) >= challenge.getMaxTimes()) { + user.sendMessage("challenges.not-repeatable"); + return new ChallengeResult(); } // Check repeatability if (manager.isChallengeComplete(user, challenge.getUniqueId()) && (!challenge.isRepeatable() || challenge.getChallengeType().equals(ChallengeType.LEVEL) || challenge.getChallengeType().equals(ChallengeType.ISLAND))) { - user.sendMessage("challenges.errors.cannot-repeat"); + user.sendMessage("challenges.not-repeatable"); return new ChallengeResult(); } - Bukkit.getLogger().info("DEBUG: switch " + challenge.getChallengeType()); switch (challenge.getChallengeType()) { case INVENTORY: return checkInventory(user, manager, challenge); case LEVEL: - return checkLevel(user, manager, challenge); + return checkLevel(user, challenge); case ISLAND: - return checkSurrounding(user, manager, challenge); + return checkSurrounding(user, challenge); default: return new ChallengeResult(); } } private ChallengeResult checkInventory(User user, ChallengesManager manager, Challenges challenge) { - Bukkit.getLogger().info("DEBUG: Checking inventory"); // Run through inventory List required = new ArrayList<>(challenge.getRequiredItems()); for (ItemStack req : required) { // I wonder how well this works if (!user.getInventory().containsAtLeast(req, req.getAmount())) { - Bukkit.getLogger().info("DEBUG: insufficient items " + req); user.sendMessage("challenges.error.not-enough-items", "[items]", Util.prettifyText(req.getType().toString())); return new ChallengeResult(); } @@ -133,19 +130,17 @@ public class TryToComplete { user.getInventory().removeItem(items); } } - Bukkit.getLogger().info("DEBUG: Everything there!"); return new ChallengeResult().setMeetsRequirements().setRepeat(manager.isChallengeComplete(user, challenge.getUniqueId())); } - private ChallengeResult checkLevel(User user, ChallengesManager manager, Challenges challenge) { + private ChallengeResult checkLevel(User user, Challenges challenge) { // Check if the level addon is installed or not - return addon.getAddonByName("BSkyBlock-Level").map(l -> { - return ((Level)l).getIslandLevel(user.getUniqueId()) >= challenge.getReqIslandlevel() ? - new ChallengeResult().setMeetsRequirements() : new ChallengeResult(); - }).orElse(new ChallengeResult()); + return addon.getAddonByName("BSkyBlock-Level") + .map(l -> ((Level)l).getIslandLevel(user.getUniqueId()) >= challenge.getReqIslandlevel() ? new ChallengeResult().setMeetsRequirements() : new ChallengeResult() + ).orElse(new ChallengeResult()); } - private ChallengeResult checkSurrounding(User user, ChallengesManager manager, Challenges challenge) { + private ChallengeResult checkSurrounding(User user, Challenges challenge) { if (!addon.getIslands().playerIsOnIsland(user)) { // Player is not on island user.sendMessage("challenges.error.not-on-island"); @@ -161,8 +156,7 @@ public class TryToComplete { } private ChallengeResult searchForBlocks(User user, Map map, int searchRadius) { - Map blocks = new HashMap<>(map); - addon.getLogger().info("Size of blocks = " + blocks.size()); + Map blocks = new EnumMap<>(map); for (int x = -searchRadius; x <= searchRadius; x++) { for (int y = -searchRadius; y <= searchRadius; y++) { for (int z = -searchRadius; z <= searchRadius; z++) { @@ -186,7 +180,7 @@ public class TryToComplete { } private ChallengeResult searchForEntities(User user, Map map, int searchRadius) { - Map entities = new HashMap<>(map); + Map entities = new EnumMap<>(map); user.getPlayer().getNearbyEntities(searchRadius, searchRadius, searchRadius).forEach(entity -> { // Look through all the nearby Entities, filtering by type entities.computeIfPresent(entity.getType(), (reqEntity, amount) -> amount - 1); @@ -234,18 +228,15 @@ public class TryToComplete { } for (String cmd : commands) { if (cmd.startsWith("[SELF]")) { - addon.getLogger().info("Running command '" + cmd + "' as " + player.getName()); + String alert = "Running command '" + cmd + "' as " + player.getName(); + addon.getLogger().info(alert); cmd = cmd.substring(6,cmd.length()).replace("[player]", player.getName()).trim(); try { if (!player.performCommand(cmd)) { - addon.getLogger().severe("Problem executing island command executed by player - skipping!"); - addon.getLogger().severe("Command was : " + cmd); + showError(cmd); } } catch (Exception e) { - addon.getLogger().severe("Problem executing island command executed by player - skipping!"); - addon.getLogger().severe("Command was : " + cmd); - addon.getLogger().severe("Error was: " + e.getMessage()); - e.printStackTrace(); + showError(cmd); } continue; @@ -253,15 +244,17 @@ public class TryToComplete { // Substitute in any references to player try { if (!addon.getServer().dispatchCommand(addon.getServer().getConsoleSender(), cmd.replace("[player]", player.getName()))) { - addon.getLogger().severe("Problem executing challenge reward commands - skipping!"); - addon.getLogger().severe("Command was : " + cmd); + showError(cmd); } } catch (Exception e) { - addon.getLogger().severe("Problem executing challenge reward commands - skipping!"); - addon.getLogger().severe("Command was : " + cmd); - addon.getLogger().severe("Error was: " + e.getMessage()); - e.printStackTrace(); + showError(cmd); } } } + + private void showError(final String cmd) { + addon.getLogger().severe("Problem executing command executed by player - skipping!"); + addon.getLogger().severe(() -> "Command was : " + cmd); + + } } diff --git a/src/test/java/bskyblock/addon/challenges/ChallengesAddonTest.java b/src/test/java/bskyblock/addon/challenges/ChallengesAddonTest.java index 38db86a..b2fb555 100644 --- a/src/test/java/bskyblock/addon/challenges/ChallengesAddonTest.java +++ b/src/test/java/bskyblock/addon/challenges/ChallengesAddonTest.java @@ -28,7 +28,9 @@ import org.bukkit.potion.PotionData; import org.bukkit.potion.PotionType; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.powermock.modules.junit4.PowerMockRunner; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -40,6 +42,7 @@ import bskyblock.addon.challenges.database.object.Challenges.ChallengeType; * @author tastybento * */ +@RunWith(PowerMockRunner.class) public class ChallengesAddonTest { /** diff --git a/src/test/java/bskyblock/addon/challenges/ParseItemTest.java b/src/test/java/bskyblock/addon/challenges/ParseItemTest.java new file mode 100644 index 0000000..c971670 --- /dev/null +++ b/src/test/java/bskyblock/addon/challenges/ParseItemTest.java @@ -0,0 +1,137 @@ +package bskyblock.addon.challenges; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.inventory.meta.SpawnEggMeta; +import org.bukkit.plugin.PluginManager; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +public class ParseItemTest { + + private static ChallengesAddon addon; + private static ItemFactory itemFactory; + + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + addon = mock(ChallengesAddon.class); + when(addon.getLogger()).thenReturn(Logger.getAnonymousLogger()); + Server server = mock(Server.class); + World world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + SpawnEggMeta itemMeta = mock(SpawnEggMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + + } + + + @Test + public void parseItemTest() { + // Nothing test + assertNull(new ParseItem(addon, "").getItem()); + // other + assertNull(new ParseItem(addon, "::::::::::::::").getItem()); + // other + assertNull(new ParseItem(addon, "anything:anything").getItem()); + // Bad material + assertNull(new ParseItem(addon, "nosuchmaterial:2").getItem()); + + // Material + for (Material mat : Material.values()) { + ItemStack test = new ParseItem(addon, mat.name() + ":5").getItem(); + if (test.getType().toString().endsWith("_ITEM") && !mat.toString().endsWith("_ITEM")) { + assertEquals(mat.toString() + "_ITEM", test.getType().toString()); + } else { + assertEquals(mat, test.getType()); + } + } + + // Nothing amount + ItemStack test = new ParseItem(addon, "STONE:").getItem(); + assertNull(test); + + + // Test 3 + // Bad material + assertNull(new ParseItem(addon, "nosuchmaterial:2:2").getItem()); + + // Bad amount + assertNull(new ParseItem(addon, "STONE:1:sdfgsd").getItem()); + + // Missing amount = 1 + test = new ParseItem(addon, "STONE:1:").getItem(); + assertNotNull(test); // This is okay, it's just a 2 + + // Material + for (Material mat : Material.values()) { + test = new ParseItem(addon, mat.name() + ":5:5").getItem(); + if (test.getType().toString().endsWith("_ITEM") && !mat.toString().endsWith("_ITEM")) { + assertEquals(mat.toString() + "_ITEM", test.getType().toString()); + assertEquals(5, test.getDurability()); + } else { + assertEquals(mat, test.getType()); + assertEquals(5, test.getDurability()); + } + } + // Test spawn eggs + for (EntityType ent : EntityType.values()) { + if (ent.isSpawnable()) { + test = new ParseItem(addon, "MONSTER_EGG:" + ent.name() + ":3").getItem(); + assertEquals(Material.MONSTER_EGG, test.getType()); + assertEquals(3, test.getAmount()); + } + } + // Test Potions + PotionMeta itemMeta = mock(PotionMeta.class); + when(itemFactory.getItemMeta(any())).thenReturn(itemMeta); + when(Bukkit.getItemFactory()).thenReturn(itemFactory); + + // Bad material + assertNull(new ParseItem(addon, "nosuchmaterial:JUMP:2:NOTEXTENDED:NOSPLASH:2").getItem()); + // Bad amount + assertNull(new ParseItem(addon, "POTION:JUMP:2:NOTEXTENDED:NOSPLASH:asfdas").getItem()); + test = new ParseItem(addon, "POTION:JUMP:2:NOTEXTENDED:NOSPLASH:").getItem(); + assertNull(test); + + test = new ParseItem(addon, "POTION:JUMP:2:NOTEXTENDED:NOSPLASH:1").getItem(); + assertEquals(Material.POTION, test.getType()); + test = new ParseItem(addon, "POTION:STRENGTH:1:EXTENDED:SPLASH:1").getItem(); + assertEquals(Material.SPLASH_POTION, test.getType()); + test = new ParseItem(addon, "POTION:INSTANT_DAMAGE:2::LINGER:2").getItem(); + assertEquals(Material.LINGERING_POTION, test.getType()); + test = new ParseItem(addon, "TIPPED_ARROW:STRENGTH:1:::1").getItem(); + assertEquals(Material.TIPPED_ARROW, test.getType()); + } + +}