diff --git a/pom.xml b/pom.xml index 97b2f18..5df5469 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ world.bentobox challenges - 0.3.1-SNAPSHOT + 0.5.0-SNAPSHOT Challenges Challenges is an add-on for BentoBox, an expandable Minecraft Bukkit plugin for island-type games like ASkyBlock or AcidIsland. @@ -60,6 +60,10 @@ vault-repo http://nexus.hc.to/content/repositories/pub_releases + + wesjd-repo + https://nexus.wesjd.net/repository/thirdparty/ + @@ -69,6 +73,12 @@ 1.13.2-R0.1-SNAPSHOT provided + + org.spigotmc + spigot + 1.13.2-R0.1-SNAPSHOT + provided + org.mockito mockito-all @@ -90,7 +100,7 @@ world.bentobox bentobox - 1.1-SNAPSHOT + 1.1 provided @@ -111,6 +121,11 @@ + + net.wesjd + anvilgui + 1.2.1-SNAPSHOT + @@ -195,8 +210,16 @@ maven-shade-plugin 3.1.1 - false + true + + + package + + shade + + + org.apache.maven.plugins diff --git a/src/main/java/world/bentobox/challenges/ChallengesAddon.java b/src/main/java/world/bentobox/challenges/ChallengesAddon.java index 4d3f27d..6f4cc94 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesAddon.java +++ b/src/main/java/world/bentobox/challenges/ChallengesAddon.java @@ -1,13 +1,18 @@ package world.bentobox.challenges; -import org.bukkit.Bukkit; +import org.bukkit.Bukkit; +import java.util.Optional; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.bentobox.api.configuration.Config; +import world.bentobox.bentobox.hooks.VaultHook; import world.bentobox.challenges.commands.ChallengesCommand; import world.bentobox.challenges.commands.admin.Challenges; import world.bentobox.challenges.listeners.ResetListener; import world.bentobox.challenges.listeners.SaveListener; -import world.bentobox.bentobox.api.addons.Addon; -import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.level.Level; + /** * Add-on to BSkyBlock that enables challenges @@ -16,90 +21,269 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; */ public class ChallengesAddon extends Addon { +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + private ChallengesManager challengesManager; - private String permissionPrefix = "addon"; - private FreshSqueezedChallenges importManager; + + private ChallengesImportManager importManager; + + private Settings settings; + private boolean hooked; + /** + * This boolean indicate if economy is enabled. + */ + private boolean economyProvided; + + /** + * VaultHook that process economy. + * todo: because of BentoBox limitations. + */ + private Optional vaultHook = null; + + /** + * Level addon. + */ + private Level levelAddon; + + /** + * This indicate if level addon exists. + */ + private boolean levelProvided; + +// --------------------------------------------------------------------- +// Section: Constants +// --------------------------------------------------------------------- + + + /** + * Permission prefix for addon. + */ + private static final String PERMISSION_PREFIX = "addon"; + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ @Override public void onLoad() { // Save default config.yml - saveDefaultConfig(); + this.saveDefaultConfig(); + // Load the plugin's config + this.loadSettings(); } + + /** + * {@inheritDoc} + */ @Override public void onEnable() { // Check if it is enabled - it might be loaded, but not enabled. - if (getPlugin() == null || !getPlugin().isEnabled()) { + if (this.getPlugin() == null || !this.getPlugin().isEnabled()) { Bukkit.getLogger().severe("BentoBox is not available or disabled!"); this.setState(State.DISABLED); return; } // Challenges Manager - challengesManager = new ChallengesManager(this); + this.challengesManager = new ChallengesManager(this); // Challenge import setup - importManager = new FreshSqueezedChallenges(this); + this.importManager = new ChallengesImportManager(this); - // Register commands - run one tick later to allow all addons to load - // AcidIsland hook in - getPlugin().getAddonsManager().getAddonByName("AcidIsland").ifPresent(a -> { - CompositeCommand acidIslandCmd = getPlugin().getCommandsManager().getCommand("ai"); - if (acidIslandCmd != null) { - new ChallengesCommand(this, acidIslandCmd); - CompositeCommand acidCmd = getPlugin().getCommandsManager().getCommand("acid"); - new Challenges(this, acidCmd); - hooked = true; + this.getPlugin().getAddonsManager().getGameModeAddons().forEach(gameModeAddon -> { + if (!this.settings.getDisabledGameModes().contains(gameModeAddon.getDescription().getName())) + { + if (gameModeAddon.getPlayerCommand().isPresent()) + { + new ChallengesCommand(this, gameModeAddon.getPlayerCommand().get()); + this.hooked = true; + } + + if (gameModeAddon.getAdminCommand().isPresent()) + { + new Challenges(this, gameModeAddon.getAdminCommand().get()); + this.hooked = true; + } + } + }); + + if (this.hooked) { + // Try to find Level addon and if it does not exist, display a warning + + Optional level = this.getAddonByName("Level"); + + if (!level.isPresent()) + { + this.logWarning("Level add-on not found so level challenges will not work!"); + this.levelAddon = null; } - }); - getPlugin().getAddonsManager().getAddonByName("BSkyBlock").ifPresent(a -> { - // BSkyBlock hook in - CompositeCommand bsbIslandCmd = getPlugin().getCommandsManager().getCommand("island"); - if (bsbIslandCmd != null) { - new ChallengesCommand(this, bsbIslandCmd); - CompositeCommand bsbAdminCmd = getPlugin().getCommandsManager().getCommand("bsbadmin"); - new Challenges(this, bsbAdminCmd); - hooked = true; + else + { + this.levelProvided = true; + this.levelAddon = (Level) level.get(); } - }); - // If the add-on never hooks in, then it is useless - if (!hooked) { - logError("Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); + + // BentoBox limitation. Cannot check hooks, as HookManager is created after loading addons. +// Optional vault = this.getPlugin().getVault(); +// +// if (!vault.isPresent() || !vault.get().hook()) +// { +// this.vaultHook = null; +// this.logWarning("Economy plugin not found so money options will not work!"); +// } +// else +// { +// this.economyProvided = true; +// this.vaultHook = vault.get(); +// } + + // Register the reset listener + this.registerListener(new ResetListener(this)); + // Register the autosave listener. + this.registerListener(new SaveListener(this)); + } else { + this.logError("Challenges could not hook into AcidIsland or BSkyBlock so will not do anything!"); this.setState(State.DISABLED); - return; } - // Try to find Level addon and if it does not exist, display a warning - if (!getAddonByName("Level").isPresent()) { - logWarning("Level add-on not found so level challenges will not work!"); - } - // Register the reset listener - this.registerListener(new ResetListener(this)); - // Register the autosave listener. - this.registerListener(new SaveListener(this)); - // Done } + + /** + * {@inheritDoc} + */ @Override - public void onDisable(){ - if (challengesManager != null) { - challengesManager.save(); + public void onReload() + { + if (this.hooked) { + this.challengesManager.save(); + + this.loadSettings(); + this.getLogger().info("Challenges addon reloaded."); } } - public ChallengesManager getChallengesManager() { - return challengesManager; + + /** + * {@inheritDoc} + */ + @Override + public void onDisable() { + if (this.hooked) { + this.challengesManager.save(); + } + + if (this.settings != null) + { + new Config<>(this, Settings.class).saveConfigObject(this.settings); + } } + + /** + * This method loads addon configuration settings in memory. + */ + private void loadSettings() { + this.settings = new Config<>(this, Settings.class).loadConfigObject(); + + if (this.settings == null) { + // Disable + this.logError("Challenges settings could not load! Addon disabled."); + this.setState(State.DISABLED); + } + } + + +// --------------------------------------------------------------------- +// Section: Getters +// --------------------------------------------------------------------- + + + /** + * @return challengesManager + */ + public ChallengesManager getChallengesManager() { + return this.challengesManager; + } + + + /** + * @return Permission Prefix. + */ @Override public String getPermissionPrefix() { - return permissionPrefix ; + return PERMISSION_PREFIX; } + /** * @return the importManager */ - public FreshSqueezedChallenges getImportManager() { - return importManager; + public ChallengesImportManager getImportManager() { + return this.importManager; } + + /** + * @return the challenge settings. + */ + public Settings getChallengesSettings() + { + return this.settings; + } + + + /** + * + * @return economyProvided variable. + */ + public boolean isEconomyProvided() + { + if (!this.economyProvided && this.getPlugin().getVault().isPresent() && this.vaultHook == null) + { + this.vaultHook = this.getPlugin().getVault(); + this.economyProvided = this.vaultHook.get().hook(); + } + + return this.economyProvided; + } + + + /** + * Returns VaultHook. Used to get easier access to Economy. NEVER USE WITHOUT isEconomyProvided or null + * check. + * @return VaultHook or null. + */ + public VaultHook getEconomyProvider() + { + return vaultHook.orElseGet(null); + } + + + /** + * + * @return levelProvided variable. + */ + public boolean isLevelProvided() + { + return levelProvided; + } + + + /** + * This method returns Level addon. Used to easier access to Level. NEVER USE WITHOUT isLevelProvided or null + * @return LevelAddon or null. + */ + public Level getLevelAddon() + { + return levelAddon; + } } diff --git a/src/main/java/world/bentobox/challenges/FreshSqueezedChallenges.java b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java similarity index 71% rename from src/main/java/world/bentobox/challenges/FreshSqueezedChallenges.java rename to src/main/java/world/bentobox/challenges/ChallengesImportManager.java index ef2af71..c16ab32 100644 --- a/src/main/java/world/bentobox/challenges/FreshSqueezedChallenges.java +++ b/src/main/java/world/bentobox/challenges/ChallengesImportManager.java @@ -16,17 +16,21 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; -import world.bentobox.challenges.database.object.ChallengeLevels; -import world.bentobox.challenges.database.object.Challenges; +import world.bentobox.bentobox.util.ItemParser; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.Challenge; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.utils.GuiUtils; + /** * Imports challenges * @author tastybento * */ -public class FreshSqueezedChallenges { +public class ChallengesImportManager +{ private ChallengesAddon addon; private YamlConfiguration chal; @@ -35,7 +39,7 @@ public class FreshSqueezedChallenges { * Import challenges from challenges.yml * @param challengesAddon */ - public FreshSqueezedChallenges(ChallengesAddon challengesAddon) { + public ChallengesImportManager(ChallengesAddon challengesAddon) { this.addon = challengesAddon; File challengeFile = new File(addon.getDataFolder(), "challenges.yml"); if (!challengeFile.exists()) { @@ -63,13 +67,13 @@ public class FreshSqueezedChallenges { user.sendMessage("challenges.admin.import.no-load","[message]", e.getMessage()); return false; } - makeLevels(user); + makeLevels(user, world, overwrite); makeChallenges(user, world, overwrite); addon.getChallengesManager().save(); return true; } - private void makeLevels(User user) { + private void makeLevels(User user, World world, boolean overwrite) { // Parse the levels String levels = chal.getString("challenges.levels", ""); if (!levels.isEmpty()) { @@ -77,22 +81,23 @@ public class FreshSqueezedChallenges { String[] lvs = levels.split(" "); int order = 0; for (String level : lvs) { - ChallengeLevels challengeLevel = new ChallengeLevels(); + ChallengeLevel challengeLevel = new ChallengeLevel(); challengeLevel.setFriendlyName(level); - challengeLevel.setUniqueId(level); + challengeLevel.setUniqueId(Util.getWorld(world).getName() + "_" + level); challengeLevel.setOrder(order++); - challengeLevel.setWaiveramount(chal.getInt("challenges.waiveramount")); + challengeLevel.setWorld(Util.getWorld(world).getName()); + challengeLevel.setWaiverAmount(chal.getInt("challenges.waiveramount")); // Check if there is a level reward ConfigurationSection unlock = chal.getConfigurationSection("challenges.levelUnlock." + level); if (unlock != null) { challengeLevel.setUnlockMessage(unlock.getString("message")); - challengeLevel.setRewardDescription(unlock.getString("rewardDesc","")); + challengeLevel.setRewardText(unlock.getString("rewardDesc","")); challengeLevel.setRewardItems(parseItems(unlock.getString("itemReward",""))); - challengeLevel.setMoneyReward(unlock.getInt("moneyReward")); - challengeLevel.setExpReward(unlock.getInt("expReward")); + challengeLevel.setRewardMoney(unlock.getInt("moneyReward")); + challengeLevel.setRewardExperience(unlock.getInt("expReward")); challengeLevel.setRewardCommands(unlock.getStringList("commands")); } - addon.getChallengesManager().storeLevel(challengeLevel); + addon.getChallengesManager().loadLevel(challengeLevel, overwrite, user, false); } } else { user.sendMessage("challenges.admin.import.no-levels"); @@ -102,53 +107,63 @@ public class FreshSqueezedChallenges { /** * Imports challenges * @param overwrite - * @param args */ private void makeChallenges(User user, World world, boolean overwrite) { int size = 0; // Parse the challenge file ConfigurationSection chals = chal.getConfigurationSection("challenges.challengeList"); for (String challenge : chals.getKeys(false)) { - Challenges newChallenge = new Challenges(); + Challenge newChallenge = new Challenge(); newChallenge.setUniqueId(Util.getWorld(world).getName() + "_" + challenge); newChallenge.setDeployed(true); ConfigurationSection details = chals.getConfigurationSection(challenge); newChallenge.setFriendlyName(details.getString("friendlyname", challenge)); - newChallenge.setWorld(Util.getWorld(world).getName()); - 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.setDescription(GuiUtils.stringSplit(details.getString("description", ""))); + newChallenge.setIcon(ItemParser.parse(details.getString("icon") + ":1")); + + if (details.getString("type").equalsIgnoreCase("level")) + { + // Fix for older version config + newChallenge.setChallengeType(Challenge.ChallengeType.OTHER); + } + else + { + newChallenge.setChallengeType(Challenge.ChallengeType.valueOf(details.getString("type","INVENTORY").toUpperCase())); + } + newChallenge.setTakeItems(details.getBoolean("takeItems",true)); newChallenge.setRewardText(details.getString("rewardText", "")); newChallenge.setRewardCommands(details.getStringList("rewardcommands")); newChallenge.setRewardMoney(details.getInt("moneyReward",0)); - newChallenge.setRewardExp(details.getInt("expReward")); + newChallenge.setRewardExperience(details.getInt("expReward")); newChallenge.setRepeatable(details.getBoolean("repeatable")); newChallenge.setRepeatRewardText(details.getString("repeatRewardText","")); newChallenge.setRepeatMoneyReward(details.getInt("repearMoneyReward")); - newChallenge.setRepeatExpReward(details.getInt("repeatExpReward")); + newChallenge.setRepeatExperienceReward(details.getInt("repeatExpReward")); newChallenge.setRepeatRewardCommands(details.getStringList("repeatrewardcommands")); newChallenge.setMaxTimes(details.getInt("maxtimes")); // TODO reset allowed - newChallenge.setReqMoney(details.getInt("requiredMoney")); - newChallenge.setReqExp(details.getInt("requiredExp")); + newChallenge.setRequiredMoney(details.getInt("requiredMoney")); + newChallenge.setRequiredExperience(details.getInt("requiredExp")); String reqItems = details.getString("requiredItems",""); - if (newChallenge.getChallengeType().equals(Challenges.ChallengeType.INVENTORY)) { + if (newChallenge.getChallengeType().equals(Challenge.ChallengeType.INVENTORY)) { newChallenge.setRequiredItems(parseItems(reqItems)); - } else if (newChallenge.getChallengeType().equals(Challenges.ChallengeType.LEVEL)) { - newChallenge.setReqIslandlevel(Long.parseLong(reqItems)); - } else if (newChallenge.getChallengeType().equals(Challenges.ChallengeType.ISLAND)) { + } else if (newChallenge.getChallengeType().equals(Challenge.ChallengeType.OTHER)) { + newChallenge.setRequiredIslandLevel(Long.parseLong(reqItems)); + } else if (newChallenge.getChallengeType().equals(Challenge.ChallengeType.ISLAND)) { parseEntities(newChallenge, reqItems); } newChallenge.setRewardItems(parseItems(details.getString("itemReward"))); newChallenge.setRepeatItemReward(parseItems(details.getString("repeatItemReward"))); // Save - if (addon.getChallengesManager().storeChallenge(newChallenge, overwrite, user, false)) { + this.addon.getChallengesManager().addChallengeToLevel(newChallenge, + addon.getChallengesManager().getLevel(Util.getWorld(world).getName() + "_" + details.getString("level"))); + + if (addon.getChallengesManager().loadChallenge(newChallenge, overwrite, user, false)) { size++; } } - addon.getChallengesManager().sortChallenges(); + user.sendMessage("challenges.admin.import.number", "[number]", String.valueOf(size)); } @@ -157,7 +172,7 @@ public class FreshSqueezedChallenges { * @param challenge - challenge to be adjusted * @param string - string from YAML file */ - private void parseEntities(Challenges challenge, String string) { + private void parseEntities(Challenge challenge, String string) { Map req = new EnumMap<>(EntityType.class); Map blocks = new EnumMap<>(Material.class); if (!string.isEmpty()) { @@ -179,7 +194,7 @@ public class FreshSqueezedChallenges { List result = new ArrayList<>(); if (!reqList.isEmpty()) { for (String s : reqList.split(" ")) { - ItemStack item = new ParseItem(addon,s).getItem(); + ItemStack item = ItemParser.parse(s); if (item != null) { result.add(item); } @@ -187,7 +202,4 @@ public class FreshSqueezedChallenges { } return result; } - - - -} +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ChallengesManager.java b/src/main/java/world/bentobox/challenges/ChallengesManager.java index 7e2dfdb..007de11 100644 --- a/src/main/java/world/bentobox/challenges/ChallengesManager.java +++ b/src/main/java/world/bentobox/challenges/ChallengesManager.java @@ -1,517 +1,957 @@ package world.bentobox.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; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; +import java.util.*; import java.util.stream.Collectors; -import org.apache.commons.lang.WordUtils; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.challenges.commands.admin.SurroundChallengeBuilder; -import world.bentobox.challenges.database.object.ChallengeLevels; -import world.bentobox.challenges.database.object.Challenges; -import world.bentobox.challenges.database.object.Challenges.ChallengeType; -import world.bentobox.challenges.database.object.ChallengesPlayerData; -import world.bentobox.challenges.panel.ChallengesPanels; -import world.bentobox.bentobox.api.configuration.Config; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.Database; import world.bentobox.bentobox.util.Util; - -public class ChallengesManager { - - public static final String FREE = "Free"; - private Map> challengeMap; - private Config chConfig; - private Config lvConfig; - private Database players; - private ChallengesPanels challengesPanels; - private Map playerData; - private ChallengesAddon addon; - - public ChallengesManager(ChallengesAddon addon) { - this.addon = addon; - // Set up the configs - chConfig = new Config<>(addon, Challenges.class); - lvConfig = new Config<>(addon, ChallengeLevels.class); - // Players is where all the player history will be stored - players = new Database<>(addon, ChallengesPlayerData.class); - // Cache of challenges - challengeMap = new LinkedHashMap<>(); - // Cache of player data - playerData = new HashMap<>(); - load(); - } - - /** - * 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 - ChallengesPlayerData data = players.loadObject(user.getUniqueId().toString()); - // Store in cache - playerData.put(user.getUniqueId(), data); - } else { - // Create the player data - ChallengesPlayerData pd = new ChallengesPlayerData(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, World world) { - addPlayer(user); - return playerData.get(user.getUniqueId()).getTimes(world, challenge.getUniqueId()); - } - - /** - * Creates a simple example description of the requirements - * @param user - user of this command - * @param requiredItems - list of items - * @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) { - result.add(user.getTranslation("challenges.admin.create.description-item-color") + item.getAmount() + " x " + Util.prettifyText(item.getType().toString())); - } - 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; - } - Challenges newChallenge = new Challenges(); - newChallenge.setChallengeType(ChallengeType.INVENTORY); - newChallenge.setFriendlyName(inventory.getTitle()); - newChallenge.setDeployed(false); - List requiredItems = new ArrayList<>(); - inventory.forEach(item -> { - if (item != null && !item.getType().equals(Material.AIR)) { - requiredItems.add(item); - } - }); - newChallenge.setRequiredItems(requiredItems); - newChallenge.setTakeItems(true); - newChallenge.setUniqueId(inventory.getTitle()); - newChallenge.setIcon(new ItemStack(Material.MAP)); - newChallenge.setLevel(FREE); - newChallenge.setDescription(createDescription(user, requiredItems)); - - // Move all the items back to the player's inventory - inventory.forEach(item -> { - 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)); - } - }); - - // Save the challenge - if (!chConfig.saveConfigObject(newChallenge)) { - user.sendRawMessage(ChatColor.RED + "Challenge creation failed!"); - return false; - } - user.sendRawMessage("Success"); - return true; - } - - /** - * Create a surrounding challenge - * @param challengeInfo - info on the challenge from the builder - * @return true if successful, false if not - */ - public boolean createSurroundingChallenge(SurroundChallengeBuilder challengeInfo) { - if (challengeInfo.getReqBlocks().isEmpty() && challengeInfo.getReqEntities().isEmpty()) { - challengeInfo.getOwner().sendMessage("challenges.error.no-items-clicked"); - return false; - } - Challenges newChallenge = new Challenges(); - newChallenge.setChallengeType(ChallengeType.ISLAND); - newChallenge.setFriendlyName(challengeInfo.getName()); - newChallenge.setDeployed(true); - newChallenge.setRequiredBlocks(challengeInfo.getReqBlocks()); - newChallenge.setRequiredEntities(challengeInfo.getReqEntities()); - newChallenge.setUniqueId(challengeInfo.getName()); - newChallenge.setIcon(new ItemStack(Material.ARMOR_STAND)); - newChallenge.setLevel(FREE); - - // Save the challenge - if (!chConfig.saveConfigObject(newChallenge)) { - challengeInfo.getOwner().sendMessage("challenges.error.could-not-save"); - return false; - } - return true; - } - - /** - * Get the list of all challenge unique names. - * @return List of challenge names - */ - public List getAllChallengesList() { - List result = new ArrayList<>(); - challengeMap.values().forEach(ch -> ch.forEach(c -> result.add(c.getUniqueId()))); - return result; - } - - /** - * Get the list of all challenge unique names for world. - * @param world - the world to check - * @return List of challenge names - */ - public List getAllChallengesList(World world) { - List result = new ArrayList<>(); - challengeMap.values().forEach(ch -> ch.stream().filter(c -> c.getWorld().equals(Util.getWorld(world).getName())).forEach(c -> result.add(c.getUniqueId()))); - return result; - } - - /** - * Get challenge by name - * @param name - unique name of challenge - * @param world - world to check - * @return - challenge or null if it does not exist - */ - public Challenges getChallenge(String name, World world) { - String worldName = Util.getWorld(world).getName(); - for (Set ch : challengeMap.values()) { - Optional challenge = ch.stream().filter(c -> c.getUniqueId().equalsIgnoreCase(worldName + name)).findFirst(); - if (challenge.isPresent()) { - return challenge.get(); - } - } - return null; - } - - /** - * Get the status on every level - * @param user - user - * @param world - world to check - * @return Level status - how many challenges still to do on which level - */ - public List getChallengeLevelStatus(User user, World world) { - addPlayer(user); - ChallengesPlayerData pd = playerData.get(user.getUniqueId()); - List result = new ArrayList<>(); - ChallengeLevels previousLevel = null; - // 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(world, 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; - } - - /** - * Get the challenge list - * @return the challengeList - */ - public Map> getChallengeList() { - // TODO return the challenges for world - return challengeMap; - } - - /** - * Get the set of challenges for this level for this world - * @param level - the level required - * @param world - * @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, World world) { - String worldName = Util.getWorld(world).getName(); - Optional lv = challengeMap.keySet().stream().filter(l -> l.getUniqueId().equalsIgnoreCase(level)).findFirst(); - // Get the challenges applicable to this world - return lv.isPresent() ? challengeMap.get(lv.get()).stream() - .filter(c -> c.getWorld().equalsIgnoreCase(worldName) || c.getWorld().isEmpty()).collect(Collectors.toSet()) - : new HashSet<>(); - } - - /** - * @return the challengesPanels - */ - public ChallengesPanels getChallengesPanels() { - return challengesPanels; - } - - /** - * Get the previous level to the one supplied - * @param currentLevel - the current level - * @return the previous level, or null if there is none - */ - public ChallengeLevels getPreviousLevel(ChallengeLevels currentLevel) { - ChallengeLevels result = null; - for (ChallengeLevels level : challengeMap.keySet()) { - if (level.equals(currentLevel)) { - return result; - } - result = level; - } - return result; - } - - /** - * Check if a challenge exists - case insensitive - * @param name - name of challenge - * @return true if it exists, otherwise false - */ - public boolean isChallenge(String name) { - for (Set ch : challengeMap.values()) { - if (ch.stream().anyMatch(c -> c.getUniqueId().equalsIgnoreCase(name))) { - return true; - } - } - return false; - } - - /** - * Check if a challenge exists in world - case insensitive - * @param world - world to check - * @param name - name of challenge - * @return true if it exists, otherwise false - */ - public boolean isChallenge(World world, String name) { - for (Set ch : challengeMap.values()) { - if (ch.stream().filter(c -> c.getWorld().equals(Util.getWorld(world).getName())).anyMatch(c -> c.getUniqueId().equalsIgnoreCase(name))) { - return true; - } - } - return false; - } - - /** - * Checks if a challenge is complete or not - * @param uniqueId - unique ID - player's UUID - * @param challengeName - Challenge uniqueId - * @return - true if completed - */ - public boolean isChallengeComplete(User user, String challengeName, World world) { - addPlayer(user); - return playerData.get(user.getUniqueId()).isChallengeDone(world, challengeName); - } - - /** - * Check is user can see level - * @param user - user - * @param level - level unique id - * @return true if level is unlocked - */ - public boolean isLevelUnlocked(User user, String level, World world) { - addPlayer(user); - return getChallengeLevelStatus(user, world).stream().filter(LevelStatus::isUnlocked).anyMatch(lv -> lv.getLevel().getUniqueId().equalsIgnoreCase(level)); - } - - /** - * Clear and reload all challenges - */ - public void load() { - // Load the challenges - challengeMap.clear(); - addon.getLogger().info("Loading challenges..."); - chConfig.loadConfigObjects().forEach(this::storeChallenge); - sortChallenges(); - players.loadObjects().forEach(pd -> { - try { - UUID uuid = UUID.fromString(pd.getUniqueId()); - playerData.put(uuid,pd); - } catch (Exception e) { - addon.getLogger().severe("UUID for player in challenge data file is invalid!"); - } - }); - } - - /** - * Save configs and player data - */ - public void save() { - challengeMap.entrySet().forEach(en -> { - lvConfig.saveConfigObject(en.getKey()); - en.getValue().forEach(chConfig::saveConfigObject); - }); - savePlayers(); - } - - private void savePlayers() { - playerData.values().forEach(players :: saveObject); - } - - private void savePlayer(UUID playerUUID) { - if (playerData.containsKey(playerUUID)) { - players.saveObject(playerData.get(playerUUID)); - } - } - - /** - * Sets the challenge as complete and increments the number of times it has been completed - * @param user - user - * @param challengeUniqueId - unique challenge id - * @param world - world to set - */ - public void setChallengeComplete(User user, String challengeUniqueId, World world) { - addPlayer(user); - playerData.get(user.getUniqueId()).setChallengeDone(world, challengeUniqueId); - // Save - savePlayer(user.getUniqueId()); - } - - /** - * Reset the challenge to zero time / not done - * @param user - user - * @param challengeUniqueId - unique challenge id - * @param world - world to set - */ - public void setResetChallenge(User user, String challengeUniqueId, World world) { - addPlayer(user); - playerData.get(user.getUniqueId()).setChallengeTimes(world, challengeUniqueId, 0); - // Save - savePlayer(user.getUniqueId()); - } - - /** - * @param challengeList the challengeList to set - */ - public void setChallengeList(Map> challengeList) { - this.challengeMap = challengeList; - } - - public void sortChallenges() { - // Sort the challenge list into level order - challengeMap = challengeMap.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, - (oldValue, newValue) -> oldValue, LinkedHashMap::new)); - } +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.database.object.ChallengesPlayerData; +import world.bentobox.challenges.utils.LevelStatus; - /** - * Store challenge silently. Used when loading. - * @param challenge - * @return true if successful - */ - private boolean storeChallenge(Challenges challenge) { - return storeChallenge(challenge, true, null, true); - } +/** + * This class manges challenges. It allows access to all data that is stored to database. + * It also provides information about challenge level status for each user. + */ +public class ChallengesManager +{ +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- - /** - * Stores the challenge. - * @param challenge - challenge - * @param overwrite - true if previous challenge should be overwritten - * @param user - user making the request - * @param silent - if true, no messages are sent to user - * @return - true if imported - */ - public boolean storeChallenge(Challenges challenge, boolean overwrite, User user, boolean silent) { - // See if we have this level already - ChallengeLevels level; - if (lvConfig.configObjectExists(challenge.getLevel())) { - // Get it from the database - level = lvConfig.loadConfigObject(challenge.getLevel()); - } else { - // Make it - level = new ChallengeLevels(); - level.setUniqueId(challenge.getLevel()); - lvConfig.saveConfigObject(level); - } - challengeMap.putIfAbsent(level, new HashSet<>()); - if (challengeMap.get(level).contains(challenge)) { - if (!overwrite) { - if (!silent) { - user.sendMessage("challenges.admin.import.skipping", "[challenge]", challenge.getFriendlyName()); - } - return false; - } else { - if (!silent) { - user.sendMessage("challenges.admin.import.overwriting", "[challenge]", challenge.getFriendlyName()); - } - challengeMap.get(level).add(challenge); - return true; - } - } - if (!silent) { - user.sendMessage("challenges.admin.import.imported", "[challenge]", challenge.getFriendlyName()); - } - challengeMap.get(level).add(challenge); - return true; - } + /** + * This config object stores structures for challenge objects. + */ + private Database challengeDatabase; - /** - * Store a challenge level - * @param level the challenge level - */ - public void storeLevel(ChallengeLevels level) { - lvConfig.saveConfigObject(level); - } + /** + * This config object stores structures for challenge level objects. + */ + private Database levelDatabase; - /** - * 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; - } + /** + * This database allows to access player challenge data. + */ + private Database playersDatabase; - /** - * Resets all the challenges for user in world - * @param uuid - island owner's UUID - * @param world - world - */ - public void resetAllChallenges(UUID uuid, World world) { - User user = User.getInstance(uuid); - addPlayer(user); - playerData.get(user.getUniqueId()).reset(world); - // Save - savePlayer(user.getUniqueId()); + /** + * This is local cache that links challenge unique id with challenge object. + */ + private Map challengeCacheData; - } + /** + * This is local cache that links level unique id with level object. + */ + private Map levelCacheData; + + /** + * This is local cache that links UUID with corresponding player challenge data. + */ + private Map playerCacheData; + + /** + * This variable allows to access ChallengesAddon. + */ + private ChallengesAddon addon; -} +// --------------------------------------------------------------------- +// Section: Constants +// --------------------------------------------------------------------- + + + /** + * String for free Challenge Level. + */ + public static final String FREE = ""; + + +// --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * Initial constructor. Inits and loads all data. + * @param addon challenges addon. + */ + public ChallengesManager(ChallengesAddon addon) + { + this.addon = addon; + // Set up the configs + this.challengeDatabase = new Database<>(addon, Challenge.class); + this.levelDatabase = new Database<>(addon, ChallengeLevel.class); + // Players is where all the player history will be stored + this.playersDatabase = new Database<>(addon, ChallengesPlayerData.class); + + // Init all cache objects. + this.challengeCacheData = new HashMap<>(); + this.levelCacheData = new HashMap<>(); + this.playerCacheData = new HashMap<>(); + + this.load(); + } + + +// --------------------------------------------------------------------- +// Section: Loading and storing methods +// --------------------------------------------------------------------- + + + /** + * Clear and reload all challenges + */ + public void load() + { + this.challengeCacheData.clear(); + this.levelCacheData.clear(); + this.playerCacheData.clear(); + + this.addon.getLogger().info("Loading challenges..."); + + this.challengeDatabase.loadObjects().forEach(this::loadChallenge); + this.levelDatabase.loadObjects().forEach(this::loadLevel); + this.playersDatabase.loadObjects().forEach(this::loadPlayerData); + } + + + /** + * Load challenge silently. Used when loading. + * + * @param challenge Challenge that must be loaded. + * @return true if successful + */ + private void loadChallenge(Challenge challenge) + { + this.loadChallenge(challenge, true, null, true); + } + + + /** + * Load the challenge. + * + * @param challenge - challenge + * @param overwrite - true if previous challenge should be overwritten + * @param user - user making the request + * @param silent - if true, no messages are sent to user + * @return - true if imported + */ + public boolean loadChallenge(Challenge challenge, + boolean overwrite, + User user, + boolean silent) + { + if (this.challengeCacheData.containsKey(challenge.getUniqueId())) + { + if (!overwrite) + { + if (!silent) + { + user.sendMessage("challenges.admin.import.skipping", + "[object]", challenge.getFriendlyName()); + } + + return false; + } + else + { + if (!silent) + { + user.sendMessage("challenges.admin.import.overwriting", + "[object]", challenge.getFriendlyName()); + } + } + } + else + { + if (!silent) + { + user.sendMessage("challenges.admin.import.add", + "[object]", challenge.getFriendlyName()); + } + } + + this.challengeCacheData.put(challenge.getUniqueId(), challenge); + return true; + } + + + /** + * Store a challenge level + * + * @param level the challenge level + */ + private void loadLevel(ChallengeLevel level) + { + this.loadLevel(level, true, null, true); + } + + + /** + * This method loads given level into local cache. It provides functionality to + * overwrite local value with new one, and send message to given user. + * @param level of type ChallengeLevel that must be loaded in local cache. + * @param overwrite of type boolean that indicate if local element must be overwritten. + * @param user of type User who will receive messages. + * @param silent of type boolean that indicate if message to user must be sent. + * @return boolean that indicate about load status. + */ + public boolean loadLevel(ChallengeLevel level, boolean overwrite, User user, boolean silent) + { + if (!this.isValidLevel(level)) + { + user.sendMessage("challenges.admin.import.error", + "[object]", level.getFriendlyName()); + + return false; + } + + if (this.levelCacheData.containsKey(level.getUniqueId())) + { + if (!overwrite) + { + if (!silent) + { + user.sendMessage("challenges.admin.import.skipping", + "[object]", level.getFriendlyName()); + } + + return false; + } + else + { + if (!silent) + { + user.sendMessage("challenges.admin.import.overwriting", + "[object]", level.getFriendlyName()); + } + } + } + else + { + if (!silent) + { + user.sendMessage("challenges.admin.import.add", + "[object]", level.getFriendlyName()); + } + } + + this.levelCacheData.put(level.getUniqueId(), level); + return true; + } + + + /** + * This method stores PlayerData into local cache. + * @param playerData ChallengesPlayerData that must be loaded. + */ + private void loadPlayerData(ChallengesPlayerData playerData) + { + try + { + UUID uuid = UUID.fromString(playerData.getUniqueId()); + this.playerCacheData.put(uuid, playerData); + } + catch (Exception e) + { + this.addon.getLogger().severe("UUID for player in challenge data file is invalid!"); + } + } + + +// --------------------------------------------------------------------- +// Section: Other storing related methods +// --------------------------------------------------------------------- + + + /** + * This method checks if given level all challenges exists in local cache or database. + * It also checks if world where level must operate exists. + * @param level that must be validated + * @return true ir level is valid, otherwise false. + */ + private boolean isValidLevel(ChallengeLevel level) + { + if (!this.addon.getPlugin().getIWM().inWorld(Bukkit.getWorld(level.getWorld()))) + { + return false; + } + + for (String uniqueID : level.getChallenges()) + { + if (!this.challengeCacheData.containsKey(uniqueID)) + { + if (this.challengeDatabase.objectExists(uniqueID)) + { + this.loadChallenge(this.challengeDatabase.loadObject(uniqueID)); + } + else + { + return false; + } + } + } + + return true; + } + + + /** + * Load player from database into the cache or create new player data + * + * @param user - user to add + */ + private void addPlayer(User user) + { + if (this.playerCacheData.containsKey(user.getUniqueId())) + { + return; + } + + // The player is not in the cache + // Check if the player exists in the database + + if (this.playersDatabase.objectExists(user.getUniqueId().toString())) + { + // Load player from database + ChallengesPlayerData data = this.playersDatabase.loadObject(user.getUniqueId().toString()); + // Store in cache + this.playerCacheData.put(user.getUniqueId(), data); + } + else + { + // Create the player data + ChallengesPlayerData pd = new ChallengesPlayerData(user.getUniqueId().toString()); + this.playersDatabase.saveObject(pd); + // Add to cache + this.playerCacheData.put(user.getUniqueId(), pd); + } + } + + +// --------------------------------------------------------------------- +// Section: Saving methods +// --------------------------------------------------------------------- + + + /** + * This method init all cached object saving to database. + */ + public void save() + { + this.saveChallenges(); + this.saveLevels(); + this.savePlayers(); + } + + + /** + * This method saves all challenges to database. + */ + private void saveChallenges() + { + this.challengeCacheData.values().forEach(this.challengeDatabase::saveObject); + } + + + /** + * This method saves given challenge object to database. + * @param challenge object that must be saved + */ + private void saveChallenge(Challenge challenge) + { + this.challengeDatabase.saveObject(challenge); + } + + + /** + * This method saves all levels to database. + */ + private void saveLevels() + { + this.levelCacheData.values().forEach(this.levelDatabase::saveObject); + } + + + /** + * This method saves given level into database. + * @param level object that must be saved + */ + private void saveLevel(ChallengeLevel level) + { + this.levelDatabase.saveObject(level); + } + + + /** + * This method saves all players to database. + */ + private void savePlayers() + { + this.playerCacheData.values().forEach(this.playersDatabase::saveObject); + } + + + /** + * This method saves player with given UUID. + * @param playerUUID users UUID. + */ + private void savePlayer(UUID playerUUID) + { + if (this.playerCacheData.containsKey(playerUUID)) + { + this.playersDatabase.saveObject(this.playerCacheData.get(playerUUID)); + } + } + + +// --------------------------------------------------------------------- +// Section: Player Data related methods +// --------------------------------------------------------------------- + + + /** + * This method returns all players who have done at least one challenge in given world. + * @param world World in which must search challenges. + * @return List with players who have done at least on challenge. + */ + public List getPlayers(World world) + { + List allChallengeList = this.getAllChallengesNames(world); + + // This is using Database, as some users may not be in the cache. + + return this.playersDatabase.loadObjects().stream().filter(playerData -> + allChallengeList.stream().anyMatch(playerData::isChallengeDone)). + map(playerData -> Bukkit.getPlayer(UUID.fromString(playerData.getUniqueId()))). + collect(Collectors.toList()); + } + + + /** + * This method returns how many times a player has done a challenge before + * @param user - user + * @param challenge - challenge + * @return - number of times + */ + public long getChallengeTimes(User user, Challenge challenge) + { + this.addPlayer(user); + return this.playerCacheData.get(user.getUniqueId()).getTimes(challenge.getUniqueId()); + } + + + /** + * Checks if a challenge is complete or not + * + * @param user - User who must be checked. + * @param challenge - Challenge + * @return - true if completed + */ + public boolean isChallengeComplete(User user, Challenge challenge) + { + this.addPlayer(user); + return this.playerCacheData.get(user.getUniqueId()).isChallengeDone(challenge.getUniqueId()); + } + + + /** + * Get the status on every level + * + * @param user - user + * @param world - world + * @return Level status - how many challenges still to do on which level + */ + public List getChallengeLevelStatus(User user, World world) + { + this.addPlayer(user); + ChallengesPlayerData playerData = this.playerCacheData.get(user.getUniqueId()); + + List challengeLevelList = this.getLevels(world); + + List result = new ArrayList<>(); + + // The first level is always unlocked and previous for it is null. + ChallengeLevel previousLevel = null; + int doneChallengeCount = 0; + + // For each challenge level, check how many the user has done + for (ChallengeLevel level : challengeLevelList) + { + // To find how many challenges user still must do in previous level, we must + // know how many challenges there were and how many has been done. Then + // remove waiver amount to get count of challenges that still necessary to do. + + int challengesToDo = previousLevel == null ? 0 : + (previousLevel.getChallenges().size() - doneChallengeCount - level.getWaiverAmount()); + + // As level already contains unique ids of challenges, just iterate through them. + doneChallengeCount = (int) level.getChallenges().stream().filter(playerData::isChallengeDone).count(); + + result.add(new LevelStatus( + level, + previousLevel, + challengesToDo, + level.getChallenges().size() == doneChallengeCount, + challengesToDo <= 0)); + + previousLevel = level; + } + + return result; + } + + + /** + * Check is user can see given level. + * + * @param user - user + * @param world - world + * @param level - level + * @return true if level is unlocked + */ + public boolean isLevelUnlocked(User user, World world, ChallengeLevel level) + { + this.addPlayer(user); + + return this.getChallengeLevelStatus(user, world).stream(). + filter(LevelStatus::isUnlocked). + anyMatch(lv -> lv.getLevel().equals(level)); + } + + + /** + * Sets the challenge as complete and increments the number of times it has been + * completed + * + * @param user - user + * @param challenge - challenge + */ + public void setChallengeComplete(User user, Challenge challenge) + { + this.addPlayer(user); + this.playerCacheData.get(user.getUniqueId()).setChallengeDone(challenge.getUniqueId()); + // Save + this.savePlayer(user.getUniqueId()); + } + + + /** + * Reset the challenge to zero time / not done + * + * @param user - user + * @param challenge - challenge + */ + public void resetChallenge(User user, Challenge challenge) + { + this.addPlayer(user); + this.playerCacheData.get(user.getUniqueId()).setChallengeTimes(challenge.getUniqueId(), 0); + // Save + this.savePlayer(user.getUniqueId()); + } + + + + /** + * Resets all the challenges for user in world + * + * @param user - island owner's UUID + * @param world - world + */ + public void resetAllChallenges(User user, World world) + { + this.addPlayer(user); + this.playerCacheData.get(user.getUniqueId()).reset(world); + // Save + this.savePlayer(user.getUniqueId()); + } + + + /** + * This method returns if given user has been already completed given level. + * @param level Level that must be checked. + * @param user User who need to be checked. + * @return true, if level is already completed. + */ + public boolean isLevelCompleted(User user, ChallengeLevel level) + { + this.addPlayer(user); + return this.playerCacheData.get(user.getUniqueId()).isLevelDone(level.getUniqueId()); + } + + + /** + * This method checks all level challenges and checks if all challenges are done. + * @param level Level that must be checked. + * @param user User who need to be checked. + * @return true, if all challenges are done, otherwise false. + */ + public boolean validateLevelCompletion(User user, ChallengeLevel level) + { + this.addPlayer(user); + ChallengesPlayerData playerData = this.playerCacheData.get(user.getUniqueId()); + long doneChallengeCount = level.getChallenges().stream().filter(playerData::isChallengeDone).count(); + + return level.getChallenges().size() == doneChallengeCount; + } + + + /** + * This method sets given level as completed. + * @param level Level that must be completed. + * @param user User who complete level. + */ + public void setLevelComplete(User user, ChallengeLevel level) + { + this.addPlayer(user); + this.playerCacheData.get(user.getUniqueId()).addCompletedLevel(level.getUniqueId()); + // Save + this.savePlayer(user.getUniqueId()); + } + + +// --------------------------------------------------------------------- +// Section: Challenges related methods +// --------------------------------------------------------------------- + + + /** + * Get the list of all challenge unique names for world. + * + * @param world - the world to check + * @return List of challenge names + */ + public List getAllChallengesNames(World world) + { + String worldName = Util.getWorld(world).getName(); + // TODO: Probably need to check also database. + return this.challengeCacheData.values().stream(). + sorted(Comparator.comparing(Challenge::getOrder)). + filter(challenge -> challenge.getUniqueId().startsWith(worldName)). + map(Challenge::getUniqueId). + collect(Collectors.toList()); + } + + + /** + * Get the list of all challenge for world. + * + * @param world - the world to check + * @return List of challenges + */ + public List getAllChallenges(World world) + { + String worldName = Util.getWorld(world).getName(); + // TODO: Probably need to check also database. + return this.challengeCacheData.values().stream(). + sorted(Comparator.comparing(Challenge::getOrder)). + filter(challenge -> challenge.getUniqueId().startsWith(worldName)). + collect(Collectors.toList()); + } + + + /** + * Free challenges... Challenges without a level. + * @param world World in which challenges must be searched. + * @return List with free challenges in given world. + */ + public List getFreeChallenges(World world) + { + // Free Challenges hides under FREE level. + return this.getAllChallenges(world).stream(). + filter(challenge -> challenge.getLevel().equals(FREE)). + collect(Collectors.toList()); + } + + + /** + * Level which challenges must be received + * @param level Challenge level. + * @return List with challenges in given level. + */ + public List getLevelChallenges(ChallengeLevel level) + { + return level.getChallenges().stream(). + map(this::getChallenge). + filter(Objects::nonNull). + collect(Collectors.toList()); + } + + + /** + * Get challenge by name. Case sensitive + * + * @param name - unique name of challenge + * @return - challenge or null if it does not exist + */ + public Challenge getChallenge(String name) + { + if (this.challengeCacheData.containsKey(name)) + { + return this.challengeCacheData.get(name); + } + else + { + // check database. + if (this.challengeDatabase.objectExists(name)) + { + Challenge challenge = this.challengeDatabase.loadObject(name); + this.challengeCacheData.put(name, challenge); + return challenge; + } + } + + return null; + } + + + /** + * Check if a challenge exists - case insensitive + * + * @param name - name of challenge + * @return true if it exists, otherwise false + */ + public boolean containsChallenge(String name) + { + if (this.challengeCacheData.containsKey(name)) + { + return true; + } + else + { + // check database. + if (this.challengeDatabase.objectExists(name)) + { + Challenge challenge = this.challengeDatabase.loadObject(name); + this.challengeCacheData.put(name, challenge); + return true; + } + } + + return false; + } + + + /** + * This method creates and returns new challenge with given uniqueID. + * @param uniqueID - new ID for challenge. + * @return Challenge that is currently created. + */ + public Challenge createChallenge(String uniqueID) + { + if (!this.containsChallenge(uniqueID)) + { + Challenge challenge = new Challenge(); + challenge.setUniqueId(uniqueID); + + this.saveChallenge(challenge); + this.loadChallenge(challenge); + + return challenge; + } + else + { + return null; + } + } + + + /** + * This method removes challenge from cache and memory. + * @param challenge that must be removed. + */ + public void deleteChallenge(Challenge challenge) + { + if (this.challengeCacheData.containsKey(challenge.getUniqueId())) + { + this.challengeCacheData.remove(challenge.getUniqueId()); + this.challengeDatabase.deleteObject(challenge); + } + } + + +// --------------------------------------------------------------------- +// Section: Level related methods +// --------------------------------------------------------------------- + + + /** + * This method returns list of challenge levels in given world. + * @param world for which levels must be searched. + * @return List with challenges in given world. + */ + public List getLevels(World world) + { + String worldName = Util.getWorld(world).getName(); + // TODO: Probably need to check also database. + return this.levelCacheData.values().stream(). + sorted(ChallengeLevel::compareTo). + filter(challenge -> challenge.getUniqueId().startsWith(worldName)). + collect(Collectors.toList()); + } + + + /** + * Get challenge level by its challenge. + * + * @param challenge - challenge which level must be returned. + * @return - challenge level or null if it does not exist + */ + public ChallengeLevel getLevel(Challenge challenge) + { + if (!challenge.getLevel().equals(FREE)) + { + return this.getLevel(challenge.getLevel()); + } + + return new ChallengeLevel(); + } + + + /** + * Get challenge level by name. Case sensitive + * + * @param name - unique name of challenge level + * @return - challenge level or null if it does not exist + */ + public ChallengeLevel getLevel(String name) + { + if (this.levelCacheData.containsKey(name)) + { + return this.levelCacheData.get(name); + } + else + { + // check database. + if (this.levelDatabase.objectExists(name)) + { + ChallengeLevel level = this.levelDatabase.loadObject(name); + this.levelCacheData.put(name, level); + return level; + } + } + + return null; + } + + + /** + * Check if a challenge level exists - case insensitive + * + * @param name - name of challenge level + * @return true if it exists, otherwise false + */ + public boolean containsLevel(String name) + { + if (this.levelCacheData.containsKey(name)) + { + return true; + } + else + { + // check database. + if (this.levelDatabase.objectExists(name)) + { + ChallengeLevel level = this.levelDatabase.loadObject(name); + this.levelCacheData.put(name, level); + return true; + } + } + + return false; + } + + + /** + * This method adds given challenge to given challenge level. + * @param newChallenge Challenge who must change owner. + * @param newLevel Level who must add new challenge + */ + public void addChallengeToLevel(Challenge newChallenge, ChallengeLevel newLevel) + { + if (newChallenge.getLevel().equals(FREE)) + { + newLevel.getChallenges().add(newChallenge.getUniqueId()); + newChallenge.setLevel(newLevel.getUniqueId()); + + this.saveLevel(newLevel); + this.saveChallenge(newChallenge); + } + else + { + ChallengeLevel oldLevel = this.getLevel(newChallenge.getLevel()); + + if (!oldLevel.equals(newLevel)) + { + this.removeChallengeFromLevel(newChallenge, newLevel); + newLevel.getChallenges().add(newChallenge.getUniqueId()); + newChallenge.setLevel(newLevel.getUniqueId()); + + this.saveLevel(newLevel); + this.saveChallenge(newChallenge); + } + } + } + + + /** + * This method removes given challenge from given challenge level. + * @param challenge Challenge which must leave level. + * @param level level which lost challenge + */ + public void removeChallengeFromLevel(Challenge challenge, ChallengeLevel level) + { + if (level.getChallenges().contains(challenge.getUniqueId())) + { + level.getChallenges().remove(challenge.getUniqueId()); + challenge.setLevel(FREE); + this.saveLevel(level); + this.saveChallenge(challenge); + } + } + + + /** + * This method creates and returns new challenges level with given uniqueID. + * @param uniqueID - new ID for challenge level. + * @return ChallengeLevel that is currently created. + */ + public ChallengeLevel createLevel(String uniqueID) + { + if (!this.containsLevel(uniqueID)) + { + ChallengeLevel level = new ChallengeLevel(); + level.setUniqueId(uniqueID); + + this.saveLevel(level); + this.loadLevel(level); + + return level; + } + else + { + return null; + } + } + + + /** + * This method removes challenge level from cache and memory. + * @param challengeLevel Level that must be removed. + */ + public void deleteChallengeLevel(ChallengeLevel challengeLevel) + { + if (this.levelCacheData.containsKey(challengeLevel.getUniqueId())) + { + this.levelCacheData.remove(challengeLevel.getUniqueId()); + this.levelDatabase.deleteObject(challengeLevel); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/ParseItem.java b/src/main/java/world/bentobox/challenges/ParseItem.java index f3272a4..67fd806 100644 --- a/src/main/java/world/bentobox/challenges/ParseItem.java +++ b/src/main/java/world/bentobox/challenges/ParseItem.java @@ -11,7 +11,10 @@ import org.bukkit.potion.PotionType; * Used for converting config file entries to objects * @author tastybento * + * @deprecated + * @see world.bentobox.bentobox.util.ItemParser#parse(String) */ +@Deprecated public class ParseItem { private final ItemStack item; diff --git a/src/main/java/world/bentobox/challenges/Settings.java b/src/main/java/world/bentobox/challenges/Settings.java new file mode 100644 index 0000000..39c8248 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/Settings.java @@ -0,0 +1,184 @@ +package world.bentobox.challenges; + + +import java.util.HashSet; +import java.util.Set; + +import world.bentobox.bentobox.api.configuration.ConfigComment; +import world.bentobox.bentobox.api.configuration.ConfigEntry; +import world.bentobox.bentobox.api.configuration.StoreAt; +import world.bentobox.bentobox.database.objects.DataObject; + + +@StoreAt(filename="config.yml", path="addons/Challenges") +@ConfigComment("Challenges Configuration [version]") +@ConfigComment("This config file is dynamic and saved when the server is shutdown.") +@ConfigComment("You cannot edit it while the server is running because changes will") +@ConfigComment("be lost! Use in-game settings GUI or edit when server is offline.") +@ConfigComment("") +public class Settings implements DataObject +{ + @ConfigComment("") + @ConfigComment("Reset Challenges - if this is true, player's challenges will reset when they") + @ConfigComment("reset an island or if they are kicked or leave a team. Prevents exploiting the") + @ConfigComment("challenges by doing them repeatedly.") + private boolean resetChallenges = true; + + @ConfigComment("") + @ConfigComment("Broadcast 1st time challenge completion messages to all players.") + @ConfigComment("Change to false if the spam becomes too much.") + private boolean broadcastMessages = true; + + @ConfigComment("") + @ConfigComment("Remove non-repeatable challenges from the challenge GUI when complete.") + private boolean removeCompleteOneTimeChallenges = false; + + @ConfigComment("") + @ConfigComment("Add enchanted glow to completed challenges") + private boolean addCompletedGlow = true; + + @ConfigComment("") + @ConfigComment("This indicate if free challenges must be at the start (true) or at the end (false) of list.") + private boolean freeChallengesFirst = true; + + @ConfigComment("") + @ConfigComment("This list stores GameModes in which Challenges addon should not work.") + @ConfigComment("To disable addon it is necessary to write its name in new line that starts with -. Example:") + @ConfigComment("disabled-gamemodes:") + @ConfigComment(" - BSkyBlock") + @ConfigEntry(path = "disabled-gamemodes") + private Set disabledGameModes = new HashSet<>(); + + /** + * Default variable. + */ + @ConfigComment("") + private String uniqueId = "config"; + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + @Override + public String getUniqueId() + { + return this.uniqueId; + } + + + /** + * @return resetChallenges value. + */ + public boolean isResetChallenges() + { + return this.resetChallenges; + } + + + /** + * @return broadcastMessages value. + */ + public boolean isBroadcastMessages() + { + return this.broadcastMessages; + } + + + /** + * @return removeCompleteOneTimeChallenges value. + */ + public boolean isRemoveCompleteOneTimeChallenges() + { + return this.removeCompleteOneTimeChallenges; + } + + + /** + * @return addCompletedGlow value. + */ + public boolean isAddCompletedGlow() + { + return this.addCompletedGlow; + } + + + /** + * @return disabledGameModes value. + */ + public Set getDisabledGameModes() + { + return this.disabledGameModes; + } + + + /** + * @return freeChallengesFirst value. + */ + public boolean isFreeChallengesFirst() + { + return this.freeChallengesFirst; + } + + + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } + + + /** + * @param resetChallenges new resetChallenges value. + */ + public void setResetChallenges(boolean resetChallenges) + { + this.resetChallenges = resetChallenges; + } + + + /** + * @param broadcastMessages new broadcastMessages value. + */ + public void setBroadcastMessages(boolean broadcastMessages) + { + this.broadcastMessages = broadcastMessages; + } + + + /** + * @param removeCompleteOneTimeChallenges new removeCompleteOneTimeChallenges value. + */ + public void setRemoveCompleteOneTimeChallenges(boolean removeCompleteOneTimeChallenges) + { + this.removeCompleteOneTimeChallenges = removeCompleteOneTimeChallenges; + } + + + /** + * @param addCompletedGlow new addCompletedGlow value. + */ + public void setAddCompletedGlow(boolean addCompletedGlow) + { + this.addCompletedGlow = addCompletedGlow; + } + + + /** + * @param disabledGameModes new disabledGameModes value. + */ + public void setDisabledGameModes(Set disabledGameModes) + { + this.disabledGameModes = disabledGameModes; + } + + + /** + * @param freeChallengesFirst new freeChallengesFirst value. + */ + public void setFreeChallengesFirst(boolean freeChallengesFirst) + { + this.freeChallengesFirst = freeChallengesFirst; + } +} diff --git a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java index bb2cc7a..67ddf60 100644 --- a/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java +++ b/src/main/java/world/bentobox/challenges/commands/ChallengesCommand.java @@ -3,10 +3,10 @@ package world.bentobox.challenges.commands; import java.util.List; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.ChallengesPanels2; -import world.bentobox.challenges.panel.ChallengesPanels2.Mode; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.panel.user.ChallengesGUI; + public class ChallengesCommand extends CompositeCommand { public static final String CHALLENGE_COMMAND = "challenges"; @@ -19,7 +19,11 @@ public class ChallengesCommand extends CompositeCommand { public boolean execute(User user, String label, List args) { // Open up the challenges GUI if (user.isPlayer()) { - new ChallengesPanels2((ChallengesAddon) getAddon(), user, user, args.isEmpty() ? "" : args.get(0), getWorld(), getPermissionPrefix(), getTopLabel(), Mode.PLAYER); + new ChallengesGUI((ChallengesAddon) this.getAddon(), + this.getWorld(), + user, + this.getTopLabel(), + this.getPermissionPrefix()).build(); return true; } // Show help diff --git a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java b/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java index 1115ff7..a73a127 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/Challenges.java @@ -3,10 +3,10 @@ package world.bentobox.challenges.commands.admin; import java.util.List; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.panel.ChallengesPanels2; -import world.bentobox.challenges.panel.ChallengesPanels2.Mode; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.panel.admin.AdminGUI; + public class Challenges extends CompositeCommand { @@ -25,7 +25,7 @@ public class Challenges extends CompositeCommand { this.setDescription("challenges.admin.description"); // Register sub commands new ImportCommand(getAddon(), this); - new CompleteChallenge(getAddon(), this); + // new CompleteChallenge(getAddon(), this); new ReloadChallenges(getAddon(), this); new ResetChallenge(getAddon(), this); //new ShowChallenges(getAddon(), this); @@ -37,7 +37,12 @@ public class Challenges extends CompositeCommand { public boolean execute(User user, String label, List args) { // Open up the admin challenges GUI if (user.isPlayer()) { - new ChallengesPanels2((ChallengesAddon) getAddon(), user, user, args.isEmpty() ? "" : args.get(0), getWorld(), getPermissionPrefix(), getTopLabel(), Mode.ADMIN); + new AdminGUI((ChallengesAddon) this.getAddon(), + this.getWorld(), + user, + this.getTopLabel(), + this.getPermissionPrefix()).build(); + return true; } return false; diff --git a/src/main/java/world/bentobox/challenges/commands/admin/CompleteChallenge.java b/src/main/java/world/bentobox/challenges/commands/admin/CompleteChallenge.java index 486662a..1a156fe 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CompleteChallenge.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CompleteChallenge.java @@ -51,13 +51,13 @@ public class CompleteChallenge extends CompositeCommand { return false; } // Check for valid challenge name - if (!manager.isChallenge(getWorld(), args.get(1))) { + if (!manager.containsChallenge(args.get(1))) { user.sendMessage("challenges.admin.complete.unknown-challenge"); return false; } // Complete challenge User target = User.getInstance(targetUUID); - manager.setChallengeComplete(target, args.get(1), getWorld()); + manager.setChallengeComplete(target, this.manager.getChallenge(args.get(1))); user.sendMessage("general.success"); return true; } @@ -70,7 +70,7 @@ public class CompleteChallenge extends CompositeCommand { return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg)); } else if (args.size() == 4) { // Challenges in this world - return Optional.of(Util.tabLimit(manager.getAllChallengesList(getWorld()), lastArg)); + return Optional.of(Util.tabLimit(manager.getAllChallengesNames(getWorld()), lastArg)); } return Optional.empty(); } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/CreateChallenge.java b/src/main/java/world/bentobox/challenges/commands/admin/CreateChallenge.java index 2024ccc..4d3f69d 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CreateChallenge.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CreateChallenge.java @@ -9,6 +9,11 @@ import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.user.User; + +/** + * @deprecated Challenges can be creaded via GUI. + */ +@Deprecated public class CreateChallenge extends CompositeCommand { /** diff --git a/src/main/java/world/bentobox/challenges/commands/admin/CreateSurrounding.java b/src/main/java/world/bentobox/challenges/commands/admin/CreateSurrounding.java index e55c6d3..3ce860a 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/CreateSurrounding.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/CreateSurrounding.java @@ -25,8 +25,9 @@ import world.bentobox.bentobox.util.Util; /** * Command to create a surrounding type challenge * @author tastybento - * + * @deprecated Required blocks can be added via GUI. Not necessary. */ +@Deprecated public class CreateSurrounding extends CompositeCommand implements Listener { HashMap inProgress = new HashMap<>(); diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ResetChallenge.java b/src/main/java/world/bentobox/challenges/commands/admin/ResetChallenge.java index a644015..5e762d5 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ResetChallenge.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ResetChallenge.java @@ -13,6 +13,11 @@ import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.util.Util; + +/** + * @deprecated Challenges can be reset via GUI. + */ +@Deprecated public class ResetChallenge extends CompositeCommand { private ChallengesManager manager; @@ -51,13 +56,13 @@ public class ResetChallenge extends CompositeCommand { return false; } // Check for valid challenge name - if (!manager.isChallenge(getWorld(), args.get(1))) { + if (!manager.containsChallenge(args.get(1))) { user.sendMessage("challenges.admin.complete.unknown-challenge"); return false; } // Complete challenge User target = User.getInstance(targetUUID); - manager.setResetChallenge(target, args.get(1), getWorld()); + manager.resetChallenge(target, manager.getChallenge(args.get(1))); user.sendMessage("general.success"); return true; } @@ -70,7 +75,7 @@ public class ResetChallenge extends CompositeCommand { return Optional.of(Util.tabLimit(new ArrayList<>(Util.getOnlinePlayerList(user)), lastArg)); } else if (args.size() == 4) { // Challenges in this world - return Optional.of(Util.tabLimit(manager.getAllChallengesList(getWorld()), lastArg)); + return Optional.of(Util.tabLimit(manager.getAllChallengesNames(getWorld()), lastArg)); } return Optional.empty(); } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java b/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java index 8842647..97eb1bb 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/ShowChallenges.java @@ -28,7 +28,7 @@ public class ShowChallenges extends CompositeCommand { @Override public boolean execute(User user, String label, List args) { - ((ChallengesAddon)getAddon()).getChallengesManager().getAllChallengesList().forEach(user::sendRawMessage); + ((ChallengesAddon)getAddon()).getChallengesManager().getAllChallengesNames(this.getWorld()).forEach(user::sendRawMessage); return true; } diff --git a/src/main/java/world/bentobox/challenges/commands/admin/SurroundChallengeBuilder.java b/src/main/java/world/bentobox/challenges/commands/admin/SurroundChallengeBuilder.java index 5ff9bce..037b0af 100644 --- a/src/main/java/world/bentobox/challenges/commands/admin/SurroundChallengeBuilder.java +++ b/src/main/java/world/bentobox/challenges/commands/admin/SurroundChallengeBuilder.java @@ -12,8 +12,9 @@ import world.bentobox.bentobox.api.user.User; /** * Enables the state of a Surrounding Challenge to be stored as it is built * @author tastybento - * + * @deprecated Levels and challenges can be created via GUI. Not necessary command. */ +@Deprecated public class SurroundChallengeBuilder { private ChallengesAddon addon; private String name; @@ -76,7 +77,7 @@ public class SurroundChallengeBuilder { } public boolean build() { - return addon.getChallengesManager().createSurroundingChallenge(this); + return false; //addon.getChallengesManager().createSurroundingChallenge(this); } diff --git a/src/main/java/world/bentobox/challenges/database/object/Challenge.java b/src/main/java/world/bentobox/challenges/database/object/Challenge.java new file mode 100644 index 0000000..6d73716 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/database/object/Challenge.java @@ -0,0 +1,1021 @@ +package world.bentobox.challenges.database.object; + + +import com.google.gson.annotations.Expose; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.*; + +import world.bentobox.bentobox.api.configuration.ConfigComment; +import world.bentobox.bentobox.database.objects.DataObject; + + +/** + * Data object for challenges + * @author tastybento + * + */ +public class Challenge implements DataObject +{ + /** + * Empty constructor + */ + public Challenge() + { + } + + + /** + * This enum holds all Challenge Types. + */ + public enum ChallengeType + { + /** + * The player must have the items on them. + */ + INVENTORY, + + /** + * Items or required entities have to be within x blocks of the player. + */ + ISLAND, + + /** + * Other type, like required money / experience or island level. This my request + * other plugins to be setup before it could work. + */ + OTHER, + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + @ConfigComment("") + @ConfigComment("Unique name of the challenge") + @Expose + private String uniqueId = ""; + + @ConfigComment("") + @ConfigComment("The name of the challenge. May include color codes. Single line.") + @Expose + private String friendlyName = ""; + + @ConfigComment("") + @ConfigComment("Whether this challenge is deployed or not.") + @Expose + private boolean deployed; + + @ConfigComment("") + @ConfigComment("Description of the challenge. Will become the lore on the icon. Can ") + @ConfigComment("include & color codes. String List.") + @Expose + private List description = new ArrayList<>(); + + @ConfigComment("") + @ConfigComment("The icon in the GUI for this challenge. ItemStack.") + @Expose + private ItemStack icon = new ItemStack(Material.PAPER); + + @ConfigComment("") + @ConfigComment("Order of this challenge. It allows define order for challenges in") + @ConfigComment("single level. If order for challenges are equal, it will order by") + @ConfigComment("challenge unique id.") + @Expose + private int order = -1; + + @ConfigComment("") + @ConfigComment("Challenge type can be INVENTORY, OTHER or ISLAND.") + @Expose + private ChallengeType challengeType = ChallengeType.INVENTORY; + + @ConfigComment("") + @ConfigComment("List of environments where this challenge will occur: NETHER, NORMAL,") + @ConfigComment("THE_END. Leave blank for all.") + @Expose + private Set environment = new HashSet<>(); + + @ConfigComment("") + @ConfigComment("If true, the challenge will disappear from the GUI when completed") + @Expose + private boolean removeWhenCompleted; + + @ConfigComment("") + @ConfigComment("Unique challenge ID. Empty means that challenge is in free challenge list.") + @Expose + private String level = ""; + +// --------------------------------------------------------------------- +// Section: Requirement related +// --------------------------------------------------------------------- + + @ConfigComment("") + @ConfigComment("") + @ConfigComment("The required permissions to see this challenge. String list.") + @Expose + private Set requiredPermissions = new HashSet<>(); + + @ConfigComment("") + @ConfigComment("This is a map of the blocks required in a ISLAND challenge. Material,") + @ConfigComment("Integer") + @Expose + private Map requiredBlocks = new EnumMap<>(Material.class); + + @ConfigComment("") + @ConfigComment("Remove the required blocks from the island") + @Expose + private boolean removeBlocks; + + @ConfigComment("") + @ConfigComment("Any entities that must be in the area for ISLAND type challenges. ") + @ConfigComment("Map EntityType, Number") + @Expose + private Map requiredEntities = new EnumMap<>(EntityType.class); + + @ConfigComment("") + @ConfigComment("Remove the entities from the island") + @Expose + private boolean removeEntities; + + @ConfigComment("") + @ConfigComment("The items that must be in the inventory to complete the challenge. ") + @ConfigComment("ItemStack List.") + @Expose + private List requiredItems = new ArrayList<>(); + + @ConfigComment("") + @ConfigComment("Take the required items from the player") + @Expose + private boolean takeItems = true; + + @ConfigComment("") + @ConfigComment("Required experience for challenge completion.") + @Expose + private int requiredExperience = 0; + + @ConfigComment("") + @ConfigComment("Take the experience from the player") + @Expose + private boolean takeExperience; + + @ConfigComment("") + @ConfigComment("Required money for challenge completion. Economy plugins or addons") + @ConfigComment("is required for this option.") + @Expose + private int requiredMoney = 0; + + @ConfigComment("") + @ConfigComment("Take the money from the player") + @Expose + private boolean takeMoney; + + @ConfigComment("") + @ConfigComment("Required island level for challenge completion. Plugin or Addon that") + @ConfigComment("calculates island level is required for this option.") + @Expose + private long requiredIslandLevel; + + @ConfigComment("") + @ConfigComment("The number of blocks around the player to search for items on an island") + @Expose + private int searchRadius = 10; + + +// --------------------------------------------------------------------- +// Section: Rewards +// --------------------------------------------------------------------- + + @ConfigComment("") + @ConfigComment("") + @ConfigComment("If this is blank, the reward text will be auto-generated, otherwise") + @ConfigComment("this will be used.") + @Expose + private String rewardText = ""; + + + @ConfigComment("") + @ConfigComment("List of items the player will receive first time. ItemStack List.") + @Expose + private List rewardItems = new ArrayList<>(); + + @ConfigComment("") + @ConfigComment("Experience point reward") + @Expose + private int rewardExperience = 0; + + @ConfigComment("") + @ConfigComment("Money reward. Economy plugin or addon required for this option.") + @Expose + private int rewardMoney = 0; + + @ConfigComment("") + @ConfigComment("Commands to run when the player completes the challenge for the first") + @ConfigComment("time. String List") + @Expose + private List rewardCommands = new ArrayList<>(); + + +// --------------------------------------------------------------------- +// Section: Repeat Rewards +// --------------------------------------------------------------------- + + + @ConfigComment("") + @ConfigComment("") + @ConfigComment("True if the challenge is repeatable") + @Expose + private boolean repeatable; + + @ConfigComment("") + @ConfigComment("Description of the repeat rewards. If blank, it will be autogenerated.") + @Expose + private String repeatRewardText = ""; + + @ConfigComment("") + @ConfigComment("Maximum number of times the challenge can be repeated. 0 or less") + @ConfigComment("will mean infinite times.") + @Expose + private int maxTimes = 1; + + @ConfigComment("") + @ConfigComment("Repeat experience reward") + @Expose + private int repeatExperienceReward = 0; + + @ConfigComment("") + @ConfigComment("Reward items for repeating the challenge. List of ItemStacks.") + @Expose + private List repeatItemReward = new ArrayList<>(); + + @ConfigComment("") + @ConfigComment("Repeat money reward. Economy plugin or addon required for this option.") + @Expose + private int repeatMoneyReward; + + @ConfigComment("") + @ConfigComment("Commands to run when challenge is repeated. String List.") + @Expose + private List repeatRewardCommands = new ArrayList<>(); + + +// --------------------------------------------------------------------- +// Section: Getters +// --------------------------------------------------------------------- + + + /** + * @return the uniqueId + */ + @Override + public String getUniqueId() + { + return uniqueId; + } + + + /** + * @return the friendlyName + */ + public String getFriendlyName() + { + return friendlyName; + } + + + /** + * @return the deployed + */ + public boolean isDeployed() + { + return deployed; + } + + + /** + * @return the description + */ + public List getDescription() + { + return description; + } + + + /** + * @return the icon + */ + public ItemStack getIcon() + { + return icon.clone(); + } + + + /** + * @return the order + */ + public int getOrder() + { + return order; + } + + + /** + * @return the challengeType + */ + public ChallengeType getChallengeType() + { + return challengeType; + } + + + /** + * @return the environment + */ + public Set getEnvironment() + { + return environment; + } + + + /** + * @return the level + */ + public String getLevel() + { + return level; + } + + + /** + * @return the removeWhenCompleted + */ + public boolean isRemoveWhenCompleted() + { + return removeWhenCompleted; + } + + + /** + * @return the requiredPermissions + */ + public Set getRequiredPermissions() + { + return requiredPermissions; + } + + + /** + * @return the requiredBlocks + */ + public Map getRequiredBlocks() + { + return requiredBlocks; + } + + + /** + * @return the removeBlocks + */ + public boolean isRemoveBlocks() + { + return removeBlocks; + } + + + /** + * @return the requiredEntities + */ + public Map getRequiredEntities() + { + return requiredEntities; + } + + + /** + * @return the removeEntities + */ + public boolean isRemoveEntities() + { + return removeEntities; + } + + + /** + * @return the requiredItems + */ + public List getRequiredItems() + { + return requiredItems; + } + + + /** + * @return the takeItems + */ + public boolean isTakeItems() + { + return takeItems; + } + + + /** + * @return the requiredExperience + */ + public int getRequiredExperience() + { + return requiredExperience; + } + + + /** + * @return the takeExperience + */ + public boolean isTakeExperience() + { + return takeExperience; + } + + + /** + * @return the requiredMoney + */ + public int getRequiredMoney() + { + return requiredMoney; + } + + + /** + * @return the takeMoney + */ + public boolean isTakeMoney() + { + return takeMoney; + } + + + /** + * @return the requiredIslandLevel + */ + public long getRequiredIslandLevel() + { + return requiredIslandLevel; + } + + + /** + * @return the searchRadius + */ + public int getSearchRadius() + { + return searchRadius; + } + + + /** + * @return the rewardText + */ + public String getRewardText() + { + return rewardText; + } + + + /** + * @return the rewardItems + */ + public List getRewardItems() + { + return rewardItems; + } + + + /** + * @return the rewardExperience + */ + public int getRewardExperience() + { + return rewardExperience; + } + + + /** + * @return the rewardMoney + */ + public int getRewardMoney() + { + return rewardMoney; + } + + + /** + * @return the rewardCommands + */ + public List getRewardCommands() + { + return rewardCommands; + } + + + /** + * @return the repeatable + */ + public boolean isRepeatable() + { + return repeatable; + } + + + /** + * @return the repeatRewardText + */ + public String getRepeatRewardText() + { + return repeatRewardText; + } + + + /** + * @return the maxTimes + */ + public int getMaxTimes() + { + return maxTimes; + } + + + /** + * @return the repeatExperienceReward + */ + public int getRepeatExperienceReward() + { + return repeatExperienceReward; + } + + + /** + * @return the repeatItemReward + */ + public List getRepeatItemReward() + { + return repeatItemReward; + } + + + /** + * @return the repeatMoneyReward + */ + public int getRepeatMoneyReward() + { + return repeatMoneyReward; + } + + + /** + * @return the repeatRewardCommands + */ + public List getRepeatRewardCommands() + { + return repeatRewardCommands; + } + + +// --------------------------------------------------------------------- +// Section: Setters +// --------------------------------------------------------------------- + + + /** + * @param uniqueId the uniqueId to set + */ + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } + + + /** + * This method sets the friendlyName value. + * @param friendlyName the friendlyName new value. + * + */ + public void setFriendlyName(String friendlyName) + { + this.friendlyName = friendlyName; + } + + + /** + * This method sets the deployed value. + * @param deployed the deployed new value. + * + */ + public void setDeployed(boolean deployed) + { + this.deployed = deployed; + } + + + /** + * This method sets the description value. + * @param description the description new value. + * + */ + public void setDescription(List description) + { + this.description = description; + } + + + /** + * This method sets the icon value. + * @param icon the icon new value. + * + */ + public void setIcon(ItemStack icon) + { + this.icon = icon; + } + + + /** + * This method sets the order value. + * @param order the order new value. + * + */ + public void setOrder(int order) + { + this.order = order; + } + + + /** + * This method sets the challengeType value. + * @param challengeType the challengeType new value. + * + */ + public void setChallengeType(ChallengeType challengeType) + { + this.challengeType = challengeType; + } + + + /** + * This method sets the environment value. + * @param environment the environment new value. + * + */ + public void setEnvironment(Set environment) + { + this.environment = environment; + } + + + /** + * This method sets the level value. + * @param level the level new value. + */ + public void setLevel(String level) + { + this.level = level; + } + + + /** + * This method sets the removeWhenCompleted value. + * @param removeWhenCompleted the removeWhenCompleted new value. + * + */ + public void setRemoveWhenCompleted(boolean removeWhenCompleted) + { + this.removeWhenCompleted = removeWhenCompleted; + } + + + /** + * This method sets the requiredPermissions value. + * @param requiredPermissions the requiredPermissions new value. + * + */ + public void setRequiredPermissions(Set requiredPermissions) + { + this.requiredPermissions = requiredPermissions; + } + + + /** + * This method sets the requiredBlocks value. + * @param requiredBlocks the requiredBlocks new value. + * + */ + public void setRequiredBlocks(Map requiredBlocks) + { + this.requiredBlocks = requiredBlocks; + } + + + /** + * This method sets the removeBlocks value. + * @param removeBlocks the removeBlocks new value. + * + */ + public void setRemoveBlocks(boolean removeBlocks) + { + this.removeBlocks = removeBlocks; + } + + + /** + * This method sets the requiredEntities value. + * @param requiredEntities the requiredEntities new value. + * + */ + public void setRequiredEntities(Map requiredEntities) + { + this.requiredEntities = requiredEntities; + } + + + /** + * This method sets the removeEntities value. + * @param removeEntities the removeEntities new value. + * + */ + public void setRemoveEntities(boolean removeEntities) + { + this.removeEntities = removeEntities; + } + + + /** + * This method sets the requiredItems value. + * @param requiredItems the requiredItems new value. + * + */ + public void setRequiredItems(List requiredItems) + { + this.requiredItems = requiredItems; + } + + + /** + * This method sets the takeItems value. + * @param takeItems the takeItems new value. + * + */ + public void setTakeItems(boolean takeItems) + { + this.takeItems = takeItems; + } + + + /** + * This method sets the requiredExperience value. + * @param requiredExperience the requiredExperience new value. + * + */ + public void setRequiredExperience(int requiredExperience) + { + this.requiredExperience = requiredExperience; + } + + + /** + * This method sets the takeExperience value. + * @param takeExperience the takeExperience new value. + * + */ + public void setTakeExperience(boolean takeExperience) + { + this.takeExperience = takeExperience; + } + + + /** + * This method sets the requiredMoney value. + * @param requiredMoney the requiredMoney new value. + * + */ + public void setRequiredMoney(int requiredMoney) + { + this.requiredMoney = requiredMoney; + } + + + /** + * This method sets the takeMoney value. + * @param takeMoney the takeMoney new value. + * + */ + public void setTakeMoney(boolean takeMoney) + { + this.takeMoney = takeMoney; + } + + + /** + * This method sets the requiredIslandLevel value. + * @param requiredIslandLevel the requiredIslandLevel new value. + * + */ + public void setRequiredIslandLevel(long requiredIslandLevel) + { + this.requiredIslandLevel = requiredIslandLevel; + } + + + /** + * This method sets the searchRadius value. + * @param searchRadius the searchRadius new value. + * + */ + public void setSearchRadius(int searchRadius) + { + this.searchRadius = searchRadius; + } + + + /** + * This method sets the rewardText value. + * @param rewardText the rewardText new value. + * + */ + public void setRewardText(String rewardText) + { + this.rewardText = rewardText; + } + + + /** + * This method sets the rewardItems value. + * @param rewardItems the rewardItems new value. + * + */ + public void setRewardItems(List rewardItems) + { + this.rewardItems = rewardItems; + } + + + /** + * This method sets the rewardExperience value. + * @param rewardExperience the rewardExperience new value. + * + */ + public void setRewardExperience(int rewardExperience) + { + this.rewardExperience = rewardExperience; + } + + + /** + * This method sets the rewardMoney value. + * @param rewardMoney the rewardMoney new value. + * + */ + public void setRewardMoney(int rewardMoney) + { + this.rewardMoney = rewardMoney; + } + + + /** + * This method sets the rewardCommands value. + * @param rewardCommands the rewardCommands new value. + * + */ + public void setRewardCommands(List rewardCommands) + { + this.rewardCommands = rewardCommands; + } + + + /** + * This method sets the repeatable value. + * @param repeatable the repeatable new value. + * + */ + public void setRepeatable(boolean repeatable) + { + this.repeatable = repeatable; + } + + + /** + * This method sets the repeatRewardText value. + * @param repeatRewardText the repeatRewardText new value. + * + */ + public void setRepeatRewardText(String repeatRewardText) + { + this.repeatRewardText = repeatRewardText; + } + + + /** + * This method sets the maxTimes value. + * @param maxTimes the maxTimes new value. + * + */ + public void setMaxTimes(int maxTimes) + { + this.maxTimes = maxTimes; + } + + + /** + * This method sets the repeatExperienceReward value. + * @param repeatExperienceReward the repeatExperienceReward new value. + * + */ + public void setRepeatExperienceReward(int repeatExperienceReward) + { + this.repeatExperienceReward = repeatExperienceReward; + } + + + /** + * This method sets the repeatItemReward value. + * @param repeatItemReward the repeatItemReward new value. + * + */ + public void setRepeatItemReward(List repeatItemReward) + { + this.repeatItemReward = repeatItemReward; + } + + + /** + * This method sets the repeatMoneyReward value. + * @param repeatMoneyReward the repeatMoneyReward new value. + * + */ + public void setRepeatMoneyReward(int repeatMoneyReward) + { + this.repeatMoneyReward = repeatMoneyReward; + } + + + /** + * This method sets the repeatRewardCommands value. + * @param repeatRewardCommands the repeatRewardCommands new value. + * + */ + public void setRepeatRewardCommands(List repeatRewardCommands) + { + this.repeatRewardCommands = repeatRewardCommands; + } + + +// --------------------------------------------------------------------- +// Section: Other methods +// --------------------------------------------------------------------- + + + /** + * @see java.lang.Object#hashCode() + * @return int + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + + /** + * @see java.lang.Object#equals(Object) () + * @param obj of type Object + * @return boolean + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof Challenge)) + { + return false; + } + + Challenge other = (Challenge) obj; + + if (uniqueId == null) + { + return other.uniqueId == null; + } + else + { + return uniqueId.equals(other.uniqueId); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java new file mode 100644 index 0000000..de4c71b --- /dev/null +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevel.java @@ -0,0 +1,472 @@ +package world.bentobox.challenges.database.object; + + +import com.google.gson.annotations.Expose; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import world.bentobox.bentobox.api.configuration.ConfigComment; +import world.bentobox.bentobox.database.objects.DataObject; +import world.bentobox.challenges.ChallengesManager; + +/** + * Represent a challenge level + * @author tastybento + * + */ +public class ChallengeLevel implements DataObject, Comparable +{ + /** + * Constructor ChallengeLevel creates a new ChallengeLevel instance. + */ + public ChallengeLevel() + { + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + @ConfigComment("") + @ConfigComment("Level name") + @Expose + private String uniqueId = ChallengesManager.FREE; + + @ConfigComment("") + @ConfigComment("A friendly name for the level. If blank, level name is used.") + @Expose + private String friendlyName = ""; + + @ConfigComment("") + @ConfigComment("ItemStack that represents current level. Will be used as icon in GUIs.") + @Expose + private ItemStack icon = new ItemStack(Material.BOOK); + + @ConfigComment("") + @ConfigComment("World that this level applies in. String.") + @Expose + private String world = ""; + + @ConfigComment("") + @ConfigComment("The ordering of the level, lowest to highest") + @Expose + private int order; + + @ConfigComment("") + @ConfigComment("The number of undone challenges that can be left on this level before") + @ConfigComment("unlocking next level.") + @Expose + private int waiverAmount = 1; + + @ConfigComment("") + @ConfigComment("The message shown when unlocking this level. Single line string.") + @Expose + private String unlockMessage = ""; + + @ConfigComment("") + @ConfigComment("") + @ConfigComment("If this is blank, the reward text will be auto-generated, otherwise") + @ConfigComment("this will be used.") + @Expose + private String rewardText = ""; + + + @ConfigComment("") + @ConfigComment("List of items the player will receive on completing level.") + @ConfigComment("ItemStack List.") + @Expose + private List rewardItems = new ArrayList<>(); + + @ConfigComment("") + @ConfigComment("Experience point reward on completing level.") + @Expose + private int rewardExperience = 0; + + @ConfigComment("") + @ConfigComment("Money reward. Economy plugin or addon required for this option.") + @Expose + private int rewardMoney = 0; + + @ConfigComment("") + @ConfigComment("Commands to run when the player completes all challenges in current") + @ConfigComment("level. String List") + @Expose + private List rewardCommands = new ArrayList<>(); + + @ConfigComment("") + @ConfigComment("Set of all challenges that is linked with current level.") + @ConfigComment("String Set") + @Expose + private Set challenges = new HashSet<>(); + + +// --------------------------------------------------------------------- +// Section: Getters +// --------------------------------------------------------------------- + + + /** + * This method returns the uniqueId value. + * @return the value of uniqueId. + * @see DataObject#getUniqueId() + */ + @Override + public String getUniqueId() + { + return uniqueId; + } + + + /** + * This method returns the friendlyName value. + * @return the value of friendlyName. + */ + public String getFriendlyName() + { + return friendlyName; + } + + + /** + * This method returns the icon value. + * @return the value of icon. + */ + public ItemStack getIcon() + { + return icon.clone(); + } + + + /** + * This method returns the world value. + * @return the value of world. + */ + public String getWorld() + { + return world; + } + + + /** + * This method returns the order value. + * @return the value of order. + */ + public int getOrder() + { + return order; + } + + + /** + * This method returns the waiverAmount value. + * @return the value of waiverAmount. + */ + public int getWaiverAmount() + { + return waiverAmount; + } + + + /** + * This method returns the unlockMessage value. + * @return the value of unlockMessage. + */ + public String getUnlockMessage() + { + return unlockMessage; + } + + + /** + * This method returns the rewardText value. + * @return the value of rewardText. + */ + public String getRewardText() + { + return rewardText; + } + + + /** + * This method returns the rewardItems value. + * @return the value of rewardItems. + */ + public List getRewardItems() + { + return rewardItems; + } + + + /** + * This method returns the rewardExperience value. + * @return the value of rewardExperience. + */ + public int getRewardExperience() + { + return rewardExperience; + } + + + /** + * This method returns the rewardMoney value. + * @return the value of rewardMoney. + */ + public int getRewardMoney() + { + return rewardMoney; + } + + + /** + * This method returns the rewardCommands value. + * @return the value of rewardCommands. + */ + public List getRewardCommands() + { + return rewardCommands; + } + + + /** + * This method returns the challenges value. + * @return the value of challenges. + */ + public Set getChallenges() + { + return challenges; + } + + +// --------------------------------------------------------------------- +// Section: Setters +// --------------------------------------------------------------------- + + + /** + * This method sets the uniqueId value. + * @param uniqueId the uniqueId new value. + * + * @see DataObject#setUniqueId(String) + */ + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } + + + /** + * This method sets the friendlyName value. + * @param friendlyName the friendlyName new value. + * + */ + public void setFriendlyName(String friendlyName) + { + this.friendlyName = friendlyName; + } + + + /** + * This method sets the icon value. + * @param icon the icon new value. + * + */ + public void setIcon(ItemStack icon) + { + this.icon = icon; + } + + + /** + * This method sets the world value. + * @param world the world new value. + * + */ + public void setWorld(String world) + { + this.world = world; + } + + + /** + * This method sets the order value. + * @param order the order new value. + * + */ + public void setOrder(int order) + { + this.order = order; + } + + + /** + * This method sets the waiverAmount value. + * @param waiverAmount the waiverAmount new value. + * + */ + public void setWaiverAmount(int waiverAmount) + { + this.waiverAmount = waiverAmount; + } + + + /** + * This method sets the unlockMessage value. + * @param unlockMessage the unlockMessage new value. + * + */ + public void setUnlockMessage(String unlockMessage) + { + this.unlockMessage = unlockMessage; + } + + + /** + * This method sets the rewardText value. + * @param rewardText the rewardText new value. + * + */ + public void setRewardText(String rewardText) + { + this.rewardText = rewardText; + } + + + /** + * This method sets the rewardItems value. + * @param rewardItems the rewardItems new value. + * + */ + public void setRewardItems(List rewardItems) + { + this.rewardItems = rewardItems; + } + + + /** + * This method sets the rewardExperience value. + * @param rewardExperience the rewardExperience new value. + * + */ + public void setRewardExperience(int rewardExperience) + { + this.rewardExperience = rewardExperience; + } + + + /** + * This method sets the rewardMoney value. + * @param rewardMoney the rewardMoney new value. + * + */ + public void setRewardMoney(int rewardMoney) + { + this.rewardMoney = rewardMoney; + } + + + /** + * This method sets the rewardCommands value. + * @param rewardCommands the rewardCommands new value. + * + */ + public void setRewardCommands(List rewardCommands) + { + this.rewardCommands = rewardCommands; + } + + + /** + * This method sets the challenges value. + * @param challenges the challenges new value. + * + */ + public void setChallenges(Set challenges) + { + this.challenges = challenges; + } + + +// --------------------------------------------------------------------- +// Section: Other methods +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + public int compareTo(ChallengeLevel o) + { + if (this.equals(o)) + { + return 0; + } + else + { + if (this.getWorld().equals(o.getWorld())) + { + if (this.order == o.getOrder()) + { + return this.getUniqueId().compareTo(o.getUniqueId()); + } + else + { + return Integer.compare(this.order, o.getOrder()); + } + } + else + { + return this.getWorld().compareTo(o.getWorld()); + } + } + } + + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof ChallengeLevel)) + { + return false; + } + + ChallengeLevel other = (ChallengeLevel) obj; + + if (uniqueId == null) + { + return other.uniqueId == null; + } + else + { + return uniqueId.equals(other.uniqueId); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevels.java b/src/main/java/world/bentobox/challenges/database/object/ChallengeLevels.java deleted file mode 100644 index 7f77fd8..0000000 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengeLevels.java +++ /dev/null @@ -1,225 +0,0 @@ -package world.bentobox.challenges.database.object; - -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.inventory.ItemStack; - -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.bentobox.api.configuration.ConfigComment; -import world.bentobox.bentobox.database.objects.DataObject; - -/** - * Represent a challenge level - * @author tastybento - * - */ -public class ChallengeLevels implements DataObject, Comparable { - - public ChallengeLevels() {} - - @ConfigComment("A friendly name for the level. If blank, level name is used.") - private String friendlyName = ""; - - @ConfigComment("Worlds that this level applies in. String list.") - private List worlds = new ArrayList<>(); - - @ConfigComment("Commands to run when this level is completed") - private List rewardCommands = new ArrayList<>(); - - @ConfigComment("Level name") - private String uniqueId = ChallengesManager.FREE; - - @ConfigComment("The number of undone challenges that can be left on this level before unlocking next level") - private int waiveramount = 1; - - @ConfigComment("The ordering of the levels, lowest to highest") - private int order = 0; - - @ConfigComment("The message shown when unlocking this level") - private String unlockMessage = ""; - - @ConfigComment("Unlock reward description") - private String rewardDescription = ""; - - @ConfigComment("List of reward itemstacks") - private List rewardItems; - - @ConfigComment("Unlock experience reward") - private int expReward; - - @ConfigComment("Unlock money reward") - private int moneyReward; - - public String getFriendlyName() { - return friendlyName; - } - - public List getRewardCommands() { - return rewardCommands = new ArrayList<>(); - } - - @Override - public String getUniqueId() { - 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; - } - - public void setFriendlyName(String friendlyName) { - this.friendlyName = friendlyName; - } - - public void setRewardCommands(List rewardCommands) { - this.rewardCommands = rewardCommands; - } - - @Override - public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; - } - - public void setWaiveramount(int waiveramount) { - this.waiveramount = waiveramount; - } - - public int getOrder() { - return order; - } - - public void setOrder(int order) { - this.order = order; - } - - @Override - public int compareTo(ChallengeLevels o) { - return Integer.compare(this.order, o.order); - } - - /** - * @return the rewardDescription - */ - public String getRewardDescription() { - return rewardDescription; - } - - /** - * @param rewardDescription the rewardDescription to set - */ - public void setRewardDescription(String rewardDescription) { - this.rewardDescription = rewardDescription; - } - - /** - * @return the rewardItems - */ - public List getRewardItems() { - return rewardItems; - } - - /** - * @param rewardItems the rewardItems to set - */ - public void setRewardItems(List rewardItems) { - this.rewardItems = rewardItems; - } - - /** - * @return the expReward - */ - public int getExpReward() { - return expReward; - } - - /** - * @param expReward the expReward to set - */ - public void setExpReward(int expReward) { - this.expReward = expReward; - } - - /** - * @return the moneyReward - */ - public int getMoneyReward() { - return moneyReward; - } - - /** - * @param moneyReward the moneyReward to set - */ - public void setMoneyReward(int moneyReward) { - this.moneyReward = moneyReward; - } - - /** - * @return the unlockMessage - */ - public String getUnlockMessage() { - return unlockMessage; - } - - /** - * @param unlockMessage the unlockMessage to set - */ - public void setUnlockMessage(String unlockMessage) { - this.unlockMessage = unlockMessage; - } - - /** - * @return the worlds - */ - public List getWorlds() { - return worlds; - } - - /** - * @param worlds the worlds to set - */ - public void setWorlds(List worlds) { - this.worlds = worlds; - } - - /* (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/world/bentobox/challenges/database/object/Challenges.java b/src/main/java/world/bentobox/challenges/database/object/Challenges.java deleted file mode 100644 index c494516..0000000 --- a/src/main/java/world/bentobox/challenges/database/object/Challenges.java +++ /dev/null @@ -1,633 +0,0 @@ -package world.bentobox.challenges.database.object; - -import java.util.ArrayList; -import java.util.EnumMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.ItemStack; - -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.bentobox.api.configuration.ConfigComment; -import world.bentobox.bentobox.database.objects.DataObject; - -/** - * Data object for challenges - * @author tastybento - * - */ -public class Challenges implements DataObject { - - public Challenges() {} - - public enum ChallengeType { - /** - * This challenge only shows and icon in the GUI and doesn't do anything. - */ - ICON, - /** - * The player must have the items on them. - */ - INVENTORY, - /** - * The island level has to be equal or over this amount. Only works if there's an island level plugin installed. - */ - LEVEL, - /** - * Items or required entities have to be within x blocks of the player. - */ - ISLAND - } - - // The order of the fields is the order shown in the YML files - @ConfigComment("Whether this challenge is deployed or not") - private boolean deployed; - - // Description - @ConfigComment("Name of the icon and challenge. May include color codes. Single line.") - private String friendlyName = ""; - @ConfigComment("Description of the challenge. Will become the lore on the icon. Can include & color codes. String List.") - private List description = new ArrayList<>(); - @ConfigComment("The icon in the GUI for this challenge. ItemStack.") - private ItemStack icon = new ItemStack(Material.PAPER); - @ConfigComment("Icon slot where this challenge should be placed. 0 to 49. A negative value means any slot") - private int slot = -1; - - // Definition - @ConfigComment("Challenge level. Default is Free") - private String level = ChallengesManager.FREE; - @ConfigComment("Challenge type can be ICON, INVENTORY, LEVEL or ISLAND.") - private ChallengeType challengeType = ChallengeType.INVENTORY; - @ConfigComment("World where this challenge operates. List only overworld. Nether and end are automatically covered.") - private String world = ""; - @ConfigComment("List of environments where this challenge will occur: NETHER, NORMAL, THE_END. Leave blank for all.") - private List environment = new ArrayList<>(); - @ConfigComment("The required permissions to see this challenge. String list.") - private Set reqPerms = new HashSet<>(); - @ConfigComment("The number of blocks around the player to search for items on an island") - private int searchRadius = 10; - @ConfigComment("If true, the challenge will disappear from the GUI when completed") - private boolean removeWhenCompleted; - @ConfigComment("Take the required items from the player") - private boolean takeItems = true; - @ConfigComment("Take the money from the player") - private boolean takeMoney = false; - - // Requirements - @ConfigComment("This is a map of the blocks required in a ISLAND challenge. Material, Integer") - 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 ISLAND type challenges. Map EntityType, Number") - 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.") - private long reqIslandlevel; - @ConfigComment("Required money") - private int reqMoney; - - // Rewards - @ConfigComment("List of items the player will receive first time. ItemStack List.") - private List rewardItems = new ArrayList<>(); - @ConfigComment("If this is blank, the reward text will be auto-generated, otherwise this will be used.") - private String rewardText = ""; - @ConfigComment("Experience point reward") - private int rewardExp; - @ConfigComment("Money reward") - private int rewardMoney; - @ConfigComment("Commands to run when the player completes the challenge for the first time. String List") - private List rewardCommands = new ArrayList<>(); - - // Repeatable - @ConfigComment("True if the challenge is repeatable") - private boolean repeatable; - @ConfigComment("Maximum number of times the challenge can be repeated") - private int maxTimes = 1; - @ConfigComment("Repeat exp award") - private int repeatExpReward; - @ConfigComment("Reward items for repeating the challenge. List of ItemStacks.") - private List repeatItemReward = new ArrayList<>(); - @ConfigComment("Repeat money award") - private int repeatMoneyReward; - @ConfigComment("Commands to run when challenge is repeated. String List.") - private List repeatRewardCommands = new ArrayList<>(); - @ConfigComment("Description of the repeat rewards. If blank, it will be autogenerated.") - private String repeatRewardText = ""; - - - @ConfigComment("Unique name of the challenge") - private String uniqueId = ""; - - /* - * END OF SETTINGS - */ - - /** - * @return the challengeType - */ - public ChallengeType getChallengeType() { - return challengeType; - } - - /** - * @param challengeType the challengeType to set - */ - public void setChallengeType(ChallengeType challengeType) { - this.challengeType = challengeType; - } - - /** - * @return the deployed - */ - public boolean isDeployed() { - return deployed; - } - - /** - * @param deployed the deployed to set - */ - public void setDeployed(boolean deployed) { - this.deployed = deployed; - } - - /** - * @return the description - */ - public List getDescription() { - return description; - } - - /** - * @param description the description to set - */ - public void setDescription(List description) { - this.description = description; - } - - /** - * @return the expReward - */ - public int getRewardExp() { - return rewardExp; - } - - /** - * @param expReward the expReward to set - */ - public void setRewardExp(int expReward) { - this.rewardExp = expReward; - } - - /** - * @return the friendlyName - */ - public String getFriendlyName() { - return friendlyName; - } - - /** - * @param friendlyName the friendlyName to set - */ - public void setFriendlyName(String friendlyName) { - this.friendlyName = friendlyName; - } - - /** - * @return the icon - */ - public ItemStack getIcon() { - return icon != null ? icon.clone() : new ItemStack(Material.MAP); - } - - /** - * @param icon the icon to set - */ - public void setIcon(ItemStack icon) { - this.icon = icon; - } - - /** - * @return the level - */ - public String getLevel() { - return level; - } - - /** - * @param level the level to set - */ - public void setLevel(String level) { - if (level.isEmpty()) { - level = ChallengesManager.FREE; - } - this.level = level; - } - - /** - * @return the maxTimes - */ - public int getMaxTimes() { - return maxTimes; - } - - /** - * @param maxTimes the maxTimes to set - */ - public void setMaxTimes(int maxTimes) { - this.maxTimes = maxTimes; - } - - /** - * @return the moneyReward - */ - public int getRewardMoney() { - return rewardMoney; - } - - /** - * @param moneyReward the moneyReward to set - */ - public void setRewardMoney(int moneyReward) { - this.rewardMoney = moneyReward; - } - - /** - * @return the removeWhenCompleted - */ - public boolean isRemoveWhenCompleted() { - return removeWhenCompleted; - } - - /** - * @param removeWhenCompleted the removeWhenCompleted to set - */ - public void setRemoveWhenCompleted(boolean removeWhenCompleted) { - this.removeWhenCompleted = removeWhenCompleted; - } - - /** - * @return the repeatable - */ - public boolean isRepeatable() { - return repeatable; - } - - /** - * @param repeatable the repeatable to set - */ - public void setRepeatable(boolean repeatable) { - this.repeatable = repeatable; - } - - /** - * @return the repeatExpReward - */ - public int getRepeatExpReward() { - return repeatExpReward; - } - - /** - * @param repeatExpReward the repeatExpReward to set - */ - public void setRepeatExpReward(int repeatExpReward) { - this.repeatExpReward = repeatExpReward; - } - - /** - * @return the repeatItemReward - */ - public List getRepeatItemReward() { - return repeatItemReward; - } - - /** - * @param repeatItemReward the repeatItemReward to set - */ - public void setRepeatItemReward(List repeatItemReward) { - this.repeatItemReward = repeatItemReward; - } - - /** - * @return the repeatMoneyReward - */ - public int getRepeatMoneyReward() { - return repeatMoneyReward; - } - - /** - * @param repeatMoneyReward the repeatMoneyReward to set - */ - public void setRepeatMoneyReward(int repeatMoneyReward) { - this.repeatMoneyReward = repeatMoneyReward; - } - - /** - * @return the repeatRewardCommands - */ - public List getRepeatRewardCommands() { - return repeatRewardCommands; - } - - /** - * @param repeatRewardCommands the repeatRewardCommands to set - */ - public void setRepeatRewardCommands(List repeatRewardCommands) { - this.repeatRewardCommands = repeatRewardCommands; - } - - /** - * @return the repeatRewardText - */ - public String getRepeatRewardText() { - return repeatRewardText; - } - - /** - * @param repeatRewardText the repeatRewardText to set - */ - public void setRepeatRewardText(String repeatRewardText) { - this.repeatRewardText = repeatRewardText; - } - - /** - * @return the reqExp - */ - public int getReqExp() { - return reqExp; - } - - /** - * @param reqExp the reqExp to set - */ - public void setReqExp(int reqExp) { - this.reqExp = reqExp; - } - - /** - * @return the reqIslandlevel - */ - public long getReqIslandlevel() { - return reqIslandlevel; - } - - /** - * @param reqIslandlevel the reqIslandlevel to set - */ - public void setReqIslandlevel(long reqIslandlevel) { - this.reqIslandlevel = reqIslandlevel; - } - - /** - * @return the reqMoney - */ - public int getReqMoney() { - return reqMoney; - } - - /** - * @param reqMoney the reqMoney to set - */ - public void setReqMoney(int reqMoney) { - this.reqMoney = reqMoney; - } - - /** - * @return the reqPerms - */ - public Set getReqPerms() { - return reqPerms; - } - - /** - * @param reqPerms the reqPerms to set - */ - public void setReqPerms(Set reqPerms) { - this.reqPerms = reqPerms; - } - - /** - * @return the requiredItems - */ - public List getRequiredItems() { - return requiredItems; - } - - /** - * @param requiredItems the requiredItems to set - */ - public void setRequiredItems(List requiredItems) { - this.requiredItems = requiredItems; - } - - /** - * @return requiredEntities - */ - public Map getRequiredEntities() { - return requiredEntities; - } - - /** - * @param requiredEntities the requiredEntities to set - */ - public void setRequiredEntities(Map requiredEntities) { - this.requiredEntities = requiredEntities; - } - - /** - * @return the requiredBlocks - */ - public Map getRequiredBlocks() { - return requiredBlocks; - } - - /** - * @param map the requiredBlocks to set - */ - public void setRequiredBlocks(Map map) { - this.requiredBlocks = map; - } - - /** - * @return the rewardCommands - */ - public List getRewardCommands() { - return rewardCommands; - } - - /** - * @param rewardCommands the rewardCommands to set - */ - public void setRewardCommands(List rewardCommands) { - this.rewardCommands = rewardCommands; - } - - /** - * @return the itemReward - */ - public List getRewardItems() { - return rewardItems; - } - - /** - * @param itemReward the itemReward to set - */ - public void setRewardItems(List itemReward) { - this.rewardItems = itemReward; - } - - /** - * @return the rewardText - */ - public String getRewardText() { - return rewardText; - } - - /** - * @param rewardText the rewardText to set - */ - public void setRewardText(String rewardText) { - this.rewardText = rewardText; - } - - /** - * @return the searchRadius - */ - public int getSearchRadius() { - return searchRadius; - } - - /** - * @param searchRadius the searchRadius to set - */ - public void setSearchRadius(int searchRadius) { - this.searchRadius = searchRadius; - } - - /** - * @return the slot - */ - public int getSlot() { - return slot; - } - - /** - * @param slot the slot to set - */ - public void setSlot(int slot) { - this.slot = slot; - } - - /** - * @return the takeItems - */ - public boolean isTakeItems() { - return takeItems; - } - - /** - * @param takeItems the takeItems to set - */ - public void setTakeItems(boolean takeItems) { - this.takeItems = takeItems; - } - - /** - * @return the takeMoney - */ - public boolean isTakeMoney() { - return takeMoney; - } - - /** - * @param takeMoney the takeMoney to set - */ - public void setTakeMoney(boolean takeMoney) { - this.takeMoney = takeMoney; - } - - /** - * @return the environment - */ - public List getEnvironment() { - return environment; - } - - /** - * @param environment the environment to set - */ - public void setEnvironment(List environment) { - this.environment = environment; - } - - /** - * @return the worlds - */ - public String getWorld() { - return world; - } - - /** - * @param worlds the worlds to set - */ - public void setWorld(String world) { - this.world = world; - } - - /** - * @return the uniqueId - */ - @Override - public String getUniqueId() { - return uniqueId; - } - - /** - * @param uniqueId the uniqueId to set - */ - @Override - public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; - } - - /* (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; - } -} diff --git a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java index fd62f9f..9907099 100644 --- a/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java +++ b/src/main/java/world/bentobox/challenges/database/object/ChallengesPlayerData.java @@ -1,17 +1,13 @@ -/** - * - */ package world.bentobox.challenges.database.object; + +import com.google.gson.annotations.Expose; +import org.bukkit.World; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.bukkit.World; - -import com.google.gson.annotations.Expose; - import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.util.Util; @@ -20,173 +16,286 @@ import world.bentobox.bentobox.util.Util; * @author tastybento * */ -public class ChallengesPlayerData implements DataObject { +public class ChallengesPlayerData implements DataObject +{ + /** + * Constructor ChallengesPlayerData creates a new ChallengesPlayerData instance. + */ + public ChallengesPlayerData() + { + } - @Expose - private String uniqueId = ""; - /** - * Challenge map, where key = unique challenge name and Value = number of times completed - */ - @Expose - private Map challengeStatus = new HashMap<>(); - @Expose - private Map challengesTimestamp = new HashMap<>(); - @Expose - private Set levelsDone = new HashSet<>(); - // Required for bean instantiation - public ChallengesPlayerData() {} + /** + * Creates a player data entry + * + * @param uniqueId - the player's UUID in string format + */ + public ChallengesPlayerData(String uniqueId) + { + this.uniqueId = uniqueId; + } - /** - * Mark a challenge as having been completed. Will increment the number of times and timestamp - * @param world - world of challenge - * @param challengeName - unique challenge name - */ - public void setChallengeDone(World world, String challengeName) { - String name = Util.getWorld(world).getName() + challengeName; - int times = challengeStatus.getOrDefault(name, 0) + 1; - challengeStatus.put(name, times); - challengesTimestamp.put(name, System.currentTimeMillis()); - } - /** - * Set the number of times a challenge has been done - * @param world - world of challenge - * @param challengeName - unique challenge name - * @param times - the number of times to set - * - */ - public void setChallengeTimes(World world, String challengeName, int times) { - String name = Util.getWorld(world).getName() + challengeName; - challengeStatus.put(name, times); - challengesTimestamp.put(name, System.currentTimeMillis()); - } +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- - /** - * Check if a challenge has been done - * @param challengeName - unique challenge name - * @return true if done at least once - */ - public boolean isChallengeDone(World world, String challengeName) { - return getTimes(world, challengeName) > 0; - } - /** - * Check how many times a challenge has been done - * @param challengeName - unique challenge name - * @return - number of times - */ - public int getTimes(World world, String challengeName) { - return challengeStatus.getOrDefault(Util.getWorld(world).getName() + challengeName, 0); - } + /** + * This variable stores each player UUID as string. + */ + @Expose + private String uniqueId = ""; - /** - * Creates a player data entry - * @param uniqueId - the player's UUID in string format - */ - public ChallengesPlayerData(String uniqueId) { - this.uniqueId = uniqueId; - } + /** + * Challenge map, where key = unique challenge name and Value = number of times + * completed + */ + @Expose + private Map challengeStatus = new HashMap<>(); - /* (non-Javadoc) - * @see world.bentobox.bbox.database.objects.DataObject#getUniqueId() - */ - @Override - public String getUniqueId() { - return uniqueId; - } + /** + * Map of challenges completion time where key is challenges unique id and value is + * timestamp when challenge was completed last time. + */ + @Expose + private Map challengesTimestamp = new HashMap<>(); - /* (non-Javadoc) - * @see world.bentobox.bbox.database.objects.DataObject#setUniqueId(java.lang.String) - */ - @Override - public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; - } + /** + * Set of Strings that contains all challenge levels that are completed. + */ + @Expose + private Set levelsDone = new HashSet<>(); - /** - * @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; - } +// --------------------------------------------------------------------- +// Section: Getters +// --------------------------------------------------------------------- - /* (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 ChallengesPlayerData)) { - return false; - } - ChallengesPlayerData other = (ChallengesPlayerData) obj; - if (uniqueId == null) { - if (other.uniqueId != null) { - return false; - } - } else if (!uniqueId.equals(other.uniqueId)) { - return false; - } - return true; - } + /** + * @return uniqueID + * @see DataObject#getUniqueId() + */ + @Override + public String getUniqueId() + { + return uniqueId; + } - /** - * Resets all challenges and levels in world for this player - * @param world - */ - public void reset(World world) { - String worldName = Util.getWorld(world).getName(); - challengeStatus.keySet().removeIf(n -> n.startsWith(worldName)); - challengesTimestamp.keySet().removeIf(n -> n.startsWith(worldName)); - levelsDone.removeIf(n -> n.startsWith(worldName)); - } -} + /** + * This method returns the challengeStatus value. + * @return the value of challengeStatus. + */ + public Map getChallengeStatus() + { + return challengeStatus; + } + + + /** + * This method returns the challengesTimestamp value. + * @return the value of challengesTimestamp. + */ + public Map getChallengesTimestamp() + { + return challengesTimestamp; + } + + + /** + * This method returns the levelsDone value. + * @return the value of levelsDone. + */ + public Set getLevelsDone() + { + return levelsDone; + } + + +// --------------------------------------------------------------------- +// Section: Setters +// --------------------------------------------------------------------- + + + /** + * @param uniqueId - unique ID the uniqueId to set + * @see DataObject#setUniqueId(String) + */ + @Override + public void setUniqueId(String uniqueId) + { + this.uniqueId = uniqueId; + } + + + /** + * This method sets the challengeStatus value. + * @param challengeStatus the challengeStatus new value. + * + */ + public void setChallengeStatus(Map challengeStatus) + { + this.challengeStatus = challengeStatus; + } + + + /** + * This method sets the challengesTimestamp value. + * @param challengesTimestamp the challengesTimestamp new value. + * + */ + public void setChallengesTimestamp(Map challengesTimestamp) + { + this.challengesTimestamp = challengesTimestamp; + } + + + /** + * This method sets the levelsDone value. + * @param levelsDone the levelsDone new value. + * + */ + public void setLevelsDone(Set levelsDone) + { + this.levelsDone = levelsDone; + } + + +// --------------------------------------------------------------------- +// Section: Other Methods +// --------------------------------------------------------------------- + + + /** + * Resets all challenges and levels in world for this player + * + * @param world world which challenges must be reset. + */ + public void reset(World world) + { + String worldName = Util.getWorld(world).getName(); + challengeStatus.keySet().removeIf(n -> n.startsWith(worldName)); + challengesTimestamp.keySet().removeIf(n -> n.startsWith(worldName)); + levelsDone.removeIf(n -> n.startsWith(worldName)); + } + + + /** + * 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()); + } + + + /** + * Set the number of times a challenge has been done + * + * @param challengeName - unique challenge name + * @param times - the number of times to set + */ + public void setChallengeTimes(String challengeName, int times) + { + 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 this.getTimes(challengeName) > 0; + } + + + /** + * 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); + } + + + /** + * This method adds given level id to completed level set. + * @param uniqueId from ChallengeLevel object. + */ + public void addCompletedLevel(String uniqueId) + { + this.levelsDone.add(uniqueId); + } + + + /** + * This method returns if given level is done. + * @param uniqueId of ChallengeLevel object. + * @return true if level is completed, otherwise false + */ + public boolean isLevelDone(String uniqueId) + { + return !this.levelsDone.isEmpty() && this.levelsDone.contains(uniqueId); + } + + + /** + * @see Object#hashCode() + * @return object hashCode value. + */ + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((uniqueId == null) ? 0 : uniqueId.hashCode()); + return result; + } + + + /** + * @see java.lang.Object#equals(java.lang.Object) + * @param obj Other object. + * @return boolean that indicate if objects are equals. + */ + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof ChallengesPlayerData)) + { + return false; + } + + ChallengesPlayerData other = (ChallengesPlayerData) obj; + + if (uniqueId == null) + { + return other.uniqueId == null; + } + else + { + return uniqueId.equals(other.uniqueId); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java index 74c2339..4ee56a9 100644 --- a/src/main/java/world/bentobox/challenges/listeners/ResetListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/ResetListener.java @@ -7,6 +7,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import world.bentobox.bentobox.api.user.User; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.bentobox.api.events.island.IslandEvent; import world.bentobox.bentobox.api.events.island.IslandEvent.Reason; @@ -26,8 +27,8 @@ public class ResetListener implements Listener { @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onIslandReset(IslandEvent e) { - if (e.getReason().equals(Reason.CREATED) || (addon.getConfig().getBoolean("resetchallenges") && e.getReason().equals(Reason.RESETTED))) { - addon.getChallengesManager().resetAllChallenges(e.getOwner(), e.getLocation().getWorld()); + if (e.getReason().equals(Reason.CREATED) || (addon.getChallengesSettings().isResetChallenges() && e.getReason().equals(Reason.RESETTED))) { + addon.getChallengesManager().resetAllChallenges(User.getInstance(e.getOwner()), e.getLocation().getWorld()); } } } diff --git a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java index 5f411ed..313d2ba 100644 --- a/src/main/java/world/bentobox/challenges/listeners/SaveListener.java +++ b/src/main/java/world/bentobox/challenges/listeners/SaveListener.java @@ -23,7 +23,7 @@ public class SaveListener implements Listener @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) public void onWorldSave(WorldSaveEvent e) { - if (!this.addon.getChallengesManager().getAllChallengesList(e.getWorld()).isEmpty()) + if (!this.addon.getChallengesManager().getAllChallenges(e.getWorld()).isEmpty()) { this.addon.getChallengesManager().save(); } diff --git a/src/main/java/world/bentobox/challenges/panel/AdminEditGUI.java b/src/main/java/world/bentobox/challenges/panel/AdminEditGUI.java index 49bc3f5..2b3fd09 100644 --- a/src/main/java/world/bentobox/challenges/panel/AdminEditGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/AdminEditGUI.java @@ -6,18 +6,23 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.database.object.Challenges; +import world.bentobox.challenges.database.object.Challenge; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; + +/** + * @deprecated All panels are reworked. + */ +@Deprecated public class AdminEditGUI implements ClickHandler { private ChallengesAddon addon; private User requester; - private Challenges challenge; + private Challenge challenge; private World world; private String permPrefix; private String label; @@ -33,7 +38,7 @@ public class AdminEditGUI implements ClickHandler { * @param permPrefix permission prefix for world * @param label command label */ - public AdminEditGUI(ChallengesAddon addon, User requester, User target, Challenges challenge, World world, + public AdminEditGUI(ChallengesAddon addon, User requester, User target, Challenge challenge, World world, String permPrefix, String label) { super(); this.addon = addon; diff --git a/src/main/java/world/bentobox/challenges/panel/AdminGUI.java b/src/main/java/world/bentobox/challenges/panel/AdminGUI.java index 2cd9940..2382d84 100644 --- a/src/main/java/world/bentobox/challenges/panel/AdminGUI.java +++ b/src/main/java/world/bentobox/challenges/panel/AdminGUI.java @@ -6,18 +6,23 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.database.object.Challenges; +import world.bentobox.challenges.database.object.Challenge; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; + +/** + * @deprecated All panels are reworked. + */ +@Deprecated public class AdminGUI implements ClickHandler { private ChallengesAddon addon; private User player; - private Challenges challenge; + private Challenge challenge; private World world; private String permPrefix; private String label; @@ -31,7 +36,7 @@ public class AdminGUI implements ClickHandler { * @param permPrefix * @param label */ - public AdminGUI(ChallengesAddon addon, User player, Challenges challenge, World world, + public AdminGUI(ChallengesAddon addon, User player, Challenge challenge, World world, String permPrefix, String label) { super(); this.addon = addon; diff --git a/src/main/java/world/bentobox/challenges/panel/ChallengesPanels.java b/src/main/java/world/bentobox/challenges/panel/ChallengesPanels.java index 85e9c68..c3d8f35 100644 --- a/src/main/java/world/bentobox/challenges/panel/ChallengesPanels.java +++ b/src/main/java/world/bentobox/challenges/panel/ChallengesPanels.java @@ -11,10 +11,12 @@ import org.bukkit.inventory.ItemStack; import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.LevelStatus; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.utils.GuiUtils; +import world.bentobox.challenges.utils.LevelStatus; import world.bentobox.challenges.commands.ChallengesCommand; -import world.bentobox.challenges.database.object.Challenges; -import world.bentobox.challenges.database.object.Challenges.ChallengeType; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; @@ -22,11 +24,15 @@ import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; +/** + * @deprecated All panels are reworked. + */ +@Deprecated public class ChallengesPanels { private ChallengesAddon addon; private ChallengesManager manager; private User user; - private String level; + private ChallengeLevel level; private World world; private String permPrefix; private String label; @@ -39,17 +45,17 @@ public class ChallengesPanels { this.permPrefix = permPrefix; this.label = label; - if (manager.getChallengeList().isEmpty()) { + if (manager.getAllChallenges(world).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(); + level = manager.getLevels(world).iterator().next().getUniqueId(); } - this.level = level; + this.level = this.manager.getLevel(level); // Check if level is valid - if (!manager.isLevelUnlocked(user, level, world)) { + if (!manager.isLevelUnlocked(user, world, this.level)) { return; } PanelBuilder panelBuilder = new PanelBuilder() @@ -65,15 +71,15 @@ public class ChallengesPanels { } private void addChallengeItems(PanelBuilder panelBuilder) { - Set levelChallenges = manager.getChallenges(level, world); + List levelChallenges = manager.getLevelChallenges(level); // Only show a control panel for the level requested. - for (Challenges challenge : levelChallenges) { + for (Challenge challenge : levelChallenges) { createItem(panelBuilder, challenge); } } private void addFreeChallanges(PanelBuilder panelBuilder) { - manager.getChallenges(ChallengesManager.FREE, world).forEach(challenge -> createItem(panelBuilder, challenge)); + manager.getFreeChallenges(world).forEach(challenge -> createItem(panelBuilder, challenge)); } @@ -81,11 +87,10 @@ public class ChallengesPanels { * 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) { + private void createItem(PanelBuilder panelBuilder, Challenge challenge) { // Check completion - boolean completed = manager.isChallengeComplete(user, challenge.getUniqueId(), world); + boolean completed = manager.isChallengeComplete(user, challenge); // If challenge is removed after completion, remove it if (completed && challenge.isRemoveWhenCompleted()) { return; @@ -96,16 +101,13 @@ public class ChallengesPanels { .description(challengeDescription(challenge)) .glow(completed) .clickHandler((panel, player, c, s) -> { - if (!challenge.getChallengeType().equals(ChallengeType.ICON)) { - new TryToComplete(addon).user(player).manager(manager).challenge(challenge) + new TryToComplete(addon).user(player).manager(manager).challenge(challenge) .world(world).permPrefix(permPrefix).label(label).build(); - //new TryToComplete(addon, player, manager, challenge, world, permPrefix, label); - } return true; }) .build(); - if (challenge.getSlot() >= 0) { - panelBuilder.item(challenge.getSlot(),item); + if (challenge.getOrder() >= 0) { + panelBuilder.item(challenge.getOrder(),item); } else { panelBuilder.item(item); } @@ -130,7 +132,7 @@ public class ChallengesPanels { PanelItem item = new PanelItemBuilder() .icon(new ItemStack(Material.ENCHANTED_BOOK)) .name(name) - .description(manager.stringSplit(user.getTranslation("challenges.navigation","[level]",name))) + .description(GuiUtils.stringSplit(user.getTranslation("challenges.navigation","[level]",name))) .clickHandler((p, u, c, s) -> { u.closeInventory(); u.performCommand(label + " " + ChallengesCommand.CHALLENGE_COMMAND + " " + status.getLevel().getUniqueId()); @@ -144,7 +146,7 @@ public class ChallengesPanels { PanelItem item = new PanelItemBuilder() .icon(new ItemStack(Material.BOOK)) .name(name) - .description(manager.stringSplit(user.getTranslation("challenges.to-complete", "[challengesToDo]",String.valueOf(previousStatus != null ? previousStatus.getNumberOfChallengesStillToDo() : ""), "[thisLevel]", previousLevelName))) + .description(GuiUtils.stringSplit(user.getTranslation("challenges.to-complete", "[challengesToDo]",String.valueOf(previousStatus != null ? previousStatus.getNumberOfChallengesStillToDo() : ""), "[thisLevel]", previousLevelName))) .build(); panelBuilder.item(item); } @@ -157,10 +159,9 @@ public class ChallengesPanels { * Creates the challenge description for the "item" in the inventory * * @param challenge - * @param player * @return List of strings splitting challenge string into 25 chars long */ - private List challengeDescription(Challenges challenge) { + private List challengeDescription(Challenge challenge) { List result = new ArrayList(); String level = challenge.getLevel(); if (!level.isEmpty()) { @@ -168,9 +169,9 @@ public class ChallengesPanels { } // Check if completed or not - boolean complete = addon.getChallengesManager().isChallengeComplete(user, challenge.getUniqueId(), world); + boolean complete = addon.getChallengesManager().isChallengeComplete(user, challenge); int maxTimes = challenge.getMaxTimes(); - long doneTimes = addon.getChallengesManager().checkChallengeTimes(user, challenge, world); + long doneTimes = addon.getChallengesManager().getChallengeTimes(user, challenge); if (complete) { result.add(user.getTranslation("challenges.complete")); } @@ -206,7 +207,7 @@ public class ChallengesPanels { // First time moneyReward = challenge.getRewardMoney(); rewardText = challenge.getRewardText(); - expReward = challenge.getRewardExp(); + expReward = challenge.getRewardExperience(); if (!rewardText.isEmpty()) { result.addAll(splitTrans(user, "challenges.first-time-rewards")); } @@ -214,7 +215,7 @@ public class ChallengesPanels { // Repeat challenge moneyReward = challenge.getRepeatMoneyReward(); rewardText = challenge.getRepeatRewardText(); - expReward = challenge.getRepeatExpReward(); + expReward = challenge.getRepeatExperienceReward(); if (!rewardText.isEmpty()) { result.addAll(splitTrans(user, "challenges.repeat-rewards")); } @@ -235,6 +236,6 @@ public class ChallengesPanels { } private Collection splitTrans(User user, String string, String...strings) { - return addon.getChallengesManager().stringSplit(user.getTranslation(string, strings)); + return GuiUtils.stringSplit(user.getTranslation(string, strings)); } } diff --git a/src/main/java/world/bentobox/challenges/panel/ChallengesPanels2.java b/src/main/java/world/bentobox/challenges/panel/ChallengesPanels2.java index 22ee2c3..2006f46 100644 --- a/src/main/java/world/bentobox/challenges/panel/ChallengesPanels2.java +++ b/src/main/java/world/bentobox/challenges/panel/ChallengesPanels2.java @@ -1,26 +1,32 @@ package world.bentobox.challenges.panel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.LevelStatus; -import world.bentobox.challenges.commands.ChallengesCommand; -import world.bentobox.challenges.database.object.Challenges; -import world.bentobox.challenges.database.object.Challenges.ChallengeType; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.commands.ChallengesCommand; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.utils.GuiUtils; +import world.bentobox.challenges.utils.LevelStatus; +/** + * @deprecated All panels are reworked. + */ +@Deprecated public class ChallengesPanels2 { public enum Mode { @@ -31,7 +37,7 @@ public class ChallengesPanels2 { private ChallengesAddon addon; private ChallengesManager manager; private User requester; - private String level; + private ChallengeLevel level; private World world; private String permPrefix; private String label; @@ -48,18 +54,18 @@ public class ChallengesPanels2 { this.label = label; this.mode = mode; - if (manager.getChallengeList().isEmpty()) { + if (manager.getAllChallenges(world).isEmpty()) { addon.getLogger().severe("There are no challenges set up!"); requester.sendMessage("general.errors.general"); return; } if (level.isEmpty()) { // TODO: open the farthest challenge panel - level = manager.getChallengeList().keySet().iterator().next().getUniqueId(); + level = manager.getLevels(world).iterator().next().getUniqueId(); } - this.level = level; + this.level = manager.getLevel(level); // Check if level is valid - if (mode.equals(Mode.PLAYER) && !manager.isLevelUnlocked(requester, level, world)) { + if (mode.equals(Mode.PLAYER) && !manager.isLevelUnlocked(requester, world, this.level)) { return; } PanelBuilder panelBuilder = new PanelBuilder(); @@ -89,13 +95,13 @@ public class ChallengesPanels2 { private void addChallengeItems(PanelBuilder panelBuilder) { // Only show a control panel for the level requested. - for (Challenges challenge : manager.getChallenges(level, world)) { + for (Challenge challenge : manager.getLevelChallenges(level)) { createItem(panelBuilder, challenge); } } private void addFreeChallanges(PanelBuilder panelBuilder) { - manager.getChallenges(ChallengesManager.FREE, world).forEach(challenge -> createItem(panelBuilder, challenge)); + manager.getFreeChallenges(world).forEach(challenge -> createItem(panelBuilder, challenge)); } @@ -103,9 +109,8 @@ public class ChallengesPanels2 { * Creates a panel item for challenge if appropriate and adds it to panelBuilder * @param panelBuilder * @param challenge - * @param requester */ - private void createItem(PanelBuilder panelBuilder, Challenges challenge) { + private void createItem(PanelBuilder panelBuilder, Challenge challenge) { // For admin, glow means activated. For user, glow means done boolean glow = false; switch (mode) { @@ -113,10 +118,10 @@ public class ChallengesPanels2 { glow = challenge.isDeployed(); break; case EDIT: - glow = manager.isChallengeComplete(requester, challenge.getUniqueId(), world); + glow = manager.isChallengeComplete(requester, challenge); break; case PLAYER: - glow = manager.isChallengeComplete(requester, challenge.getUniqueId(), world); + glow = manager.isChallengeComplete(requester, challenge); break; default: break; @@ -134,33 +139,27 @@ public class ChallengesPanels2 { if (mode.equals(Mode.ADMIN)) { // Admin click itemBuilder.clickHandler((panel, player, c, s) -> { - if (!challenge.getChallengeType().equals(ChallengeType.ICON)) { - new AdminGUI(addon, player, challenge, world, permPrefix, label); - } + new AdminGUI(addon, player, challenge, world, permPrefix, label); return true; }); } else if (mode.equals(Mode.EDIT)) { // Admin edit click itemBuilder.clickHandler((panel, player, c, s) -> { - if (!challenge.getChallengeType().equals(ChallengeType.ICON)) { - new AdminEditGUI(addon, player, target, challenge, world, permPrefix, label); - } + new AdminEditGUI(addon, player, target, challenge, world, permPrefix, label); return true; }); } else { // Player click itemBuilder.clickHandler((panel, player, c, s) -> { - if (!challenge.getChallengeType().equals(ChallengeType.ICON)) { - new TryToComplete(addon, player, manager, challenge, world, permPrefix, label); - } + new TryToComplete(addon, player, challenge, world, label, permPrefix); return true; }); } // If the challenge has a specific slot allocated, use it - if (challenge.getSlot() >= 0) { - panelBuilder.item(challenge.getSlot(),itemBuilder.build()); + if (challenge.getOrder() >= 0) { + panelBuilder.item(challenge.getOrder(),itemBuilder.build()); } else { panelBuilder.item(itemBuilder.build()); } @@ -172,7 +171,7 @@ public class ChallengesPanels2 { // Add navigation to other levels for (LevelStatus status: manager.getChallengeLevelStatus(requester, world)) { - if (status.getLevel().getUniqueId().equalsIgnoreCase(level)) { + if (status.getLevel().getUniqueId().equalsIgnoreCase(level.getUniqueId())) { // Skip if this is the current level previousStatus = status; continue; @@ -185,7 +184,7 @@ public class ChallengesPanels2 { PanelItem item = new PanelItemBuilder() .icon(new ItemStack(Material.ENCHANTED_BOOK)) .name(name) - .description(manager.stringSplit(requester.getTranslation("challenges.navigation","[level]",name))) + .description(GuiUtils.stringSplit(requester.getTranslation("challenges.navigation","[level]",name))) .clickHandler((p, u, c, s) -> { u.closeInventory(); u.performCommand(label + " " + ChallengesCommand.CHALLENGE_COMMAND + " " + status.getLevel().getUniqueId()); @@ -199,7 +198,7 @@ public class ChallengesPanels2 { PanelItem item = new PanelItemBuilder() .icon(new ItemStack(Material.BOOK)) .name(name) - .description(manager.stringSplit(requester.getTranslation("challenges.to-complete", "[challengesToDo]",String.valueOf(previousStatus != null ? previousStatus.getNumberOfChallengesStillToDo() : ""), "[thisLevel]", previousLevelName))) + .description(GuiUtils.stringSplit(requester.getTranslation("challenges.to-complete", "[challengesToDo]",String.valueOf(previousStatus != null ? previousStatus.getNumberOfChallengesStillToDo() : ""), "[thisLevel]", previousLevelName))) .build(); panelBuilder.item(item); } @@ -212,10 +211,9 @@ public class ChallengesPanels2 { * Creates the challenge description for the "item" in the inventory * * @param challenge - * @param player * @return List of strings splitting challenge string into 25 chars long */ - private List challengeDescription(Challenges challenge) { + private List challengeDescription(Challenge challenge) { List result = new ArrayList(); String level = challenge.getLevel(); if (!level.isEmpty()) { @@ -234,9 +232,9 @@ public class ChallengesPanels2 { result.addAll(addRewards(challenge, true, true)); } else { // Check if completed or not - boolean complete = addon.getChallengesManager().isChallengeComplete(requester, challenge.getUniqueId(), world); + boolean complete = addon.getChallengesManager().isChallengeComplete(requester, challenge); int maxTimes = challenge.getMaxTimes(); - long doneTimes = addon.getChallengesManager().checkChallengeTimes(requester, challenge, world); + long doneTimes = addon.getChallengesManager().getChallengeTimes(requester, challenge); if (complete) { result.add(requester.getTranslation("challenges.complete")); } @@ -274,7 +272,7 @@ public class ChallengesPanels2 { return result; } - private List addRewards(Challenges challenge, boolean complete, boolean admin) { + private List addRewards(Challenge challenge, boolean complete, boolean admin) { List result = new ArrayList<>(); double moneyReward = 0; int expReward = 0; @@ -283,7 +281,7 @@ public class ChallengesPanels2 { // First time moneyReward = challenge.getRewardMoney(); rewardText = challenge.getRewardText(); - expReward = challenge.getRewardExp(); + expReward = challenge.getRewardExperience(); if (!rewardText.isEmpty()) { result.addAll(splitTrans(requester, "challenges.first-time-rewards")); } @@ -292,7 +290,7 @@ public class ChallengesPanels2 { // Repeat challenge moneyReward = challenge.getRepeatMoneyReward(); rewardText = challenge.getRepeatRewardText(); - expReward = challenge.getRepeatExpReward(); + expReward = challenge.getRepeatExperienceReward(); if (!rewardText.isEmpty()) { result.addAll(splitTrans(requester, "challenges.repeat-rewards")); } @@ -311,6 +309,6 @@ public class ChallengesPanels2 { } private Collection splitTrans(User user, String string, String...strings) { - return addon.getChallengesManager().stringSplit(user.getTranslation(string, strings)); + return GuiUtils.stringSplit(user.getTranslation(string, strings)); } } diff --git a/src/main/java/world/bentobox/challenges/panel/CommonGUI.java b/src/main/java/world/bentobox/challenges/panel/CommonGUI.java new file mode 100644 index 0000000..5e08cbe --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/CommonGUI.java @@ -0,0 +1,245 @@ +package world.bentobox.challenges.panel; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import java.util.Collections; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; + + +/** + * This class contains common methods that will be used over all GUIs. It also allows + * easier navigation between different GUIs. + */ +public abstract class CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This variable stores parent gui. + */ + private CommonGUI parentGUI; + + /** + * Variable stores Challenges addon. + */ + protected ChallengesAddon addon; + + /** + * Variable stores world in which panel is referred to. + */ + protected World world; + + /** + * Variable stores user who created this panel. + */ + protected User user; + + /** + * Variable stores top label of command from which panel was called. + */ + protected String topLabel; + + /** + * Variable stores permission prefix of command from which panel was called. + */ + protected String permissionPrefix; + + /** + * Variable stores any value. + */ + protected Object valueObject; + + /** + * This object holds current page index. + */ + protected int pageIndex; + + /** + * This object holds PanelItem that allows to return to previous panel. + */ + protected PanelItem returnButton; + + + /** + * This enum contains buttons that is offten used in multiple GUIs. + */ + protected enum CommonButtons + { + NEXT, + PREVIOUS, + RETURN + } + + +// --------------------------------------------------------------------- +// Section: Constants +// --------------------------------------------------------------------- + + + protected static final String ADMIN = "admin"; + + protected static final String CHALLENGES = "challenges"; + + protected static final String IMPORT = "import"; + + protected static final String SETTINGS = "settings"; + + protected static final String DELETE = "delete"; + + protected static final String EDIT = "edit"; + + protected static final String ADD = "add"; + + protected static final String RESET = "reset"; + + protected static final String COMPLETE = "complete"; + + +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + + /** + * Default constructor that inits panels with minimal requirements, without parent panel. + * + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + */ + public CommonGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, topLabel, permissionPrefix, null); + } + + + /** + * Default constructor that inits panels with minimal requirements. + * + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + * @param parentGUI Parent panel for current panel. + */ + public CommonGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + this.addon = addon; + this.world = world; + this.user = user; + + this.topLabel = topLabel; + this.permissionPrefix = permissionPrefix; + + this.parentGUI = parentGUI; + + this.pageIndex = 0; + + this.returnButton = new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.buttons.back")). + icon(Material.OAK_DOOR). + clickHandler((panel, user1, clickType, i) -> { + if (this.parentGUI == null) + { + this.user.closeInventory(); + return true; + } + + this.parentGUI.build(); + return true; + }).build(); + } + + +// --------------------------------------------------------------------- +// Section: Common methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + public abstract void build(); + + + /** + * This method returns PanelItem that represents given Button. + * @param button Button that must be returned. + * @return PanelItem with requested functionality. + */ + protected PanelItem getButton(CommonButtons button) + { + ItemStack icon; + String name; + List description; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case NEXT: + { + name = this.user.getTranslation("challenges.gui.buttons.next"); + description = Collections.emptyList(); + icon = new ItemStack(Material.SIGN); + clickHandler = (panel, user, clickType, slot) -> { + this.pageIndex++; + this.build(); + return true; + }; + + break; + } + case PREVIOUS: + { + name = this.user.getTranslation("challenges.gui.buttons.previous"); + description = Collections.emptyList(); + icon = new ItemStack(Material.SIGN); + clickHandler = (panel, user, clickType, slot) -> { + this.pageIndex--; + this.build(); + return true; + }; + + break; + } + case RETURN: + return this.returnButton; + default: + return null; + } + + return new PanelItem(icon, name, description, false, clickHandler, false); + } + + + /** + * This method sets new value to ValueObject variable. + * @param value new Value of valueObject. + */ + public void setValue(Object value) + { + this.valueObject = value; + } +} + diff --git a/src/main/java/world/bentobox/challenges/panel/CreateChallengeListener.java b/src/main/java/world/bentobox/challenges/panel/CreateChallengeListener.java index feb0621..d0b63ee 100644 --- a/src/main/java/world/bentobox/challenges/panel/CreateChallengeListener.java +++ b/src/main/java/world/bentobox/challenges/panel/CreateChallengeListener.java @@ -7,6 +7,11 @@ import world.bentobox.challenges.ChallengesAddon; import world.bentobox.bentobox.api.panels.PanelListener; import world.bentobox.bentobox.api.user.User; + +/** + * @deprecated All panels are reworked. + */ +@Deprecated public class CreateChallengeListener implements PanelListener { private ChallengesAddon addon; @@ -24,7 +29,7 @@ public class CreateChallengeListener implements PanelListener { @Override public void onInventoryClose(InventoryCloseEvent event) { - addon.getChallengesManager().createInvChallenge(user, event.getInventory()); + addon.getChallengesManager().createChallenge("uniqueID"); } @Override diff --git a/src/main/java/world/bentobox/challenges/panel/CreateChallengePanel.java b/src/main/java/world/bentobox/challenges/panel/CreateChallengePanel.java index 93d8bb3..1c7473a 100644 --- a/src/main/java/world/bentobox/challenges/panel/CreateChallengePanel.java +++ b/src/main/java/world/bentobox/challenges/panel/CreateChallengePanel.java @@ -4,6 +4,11 @@ import world.bentobox.challenges.ChallengesAddon; import world.bentobox.bentobox.api.panels.builders.PanelBuilder; import world.bentobox.bentobox.api.user.User; + +/** + * @deprecated All panels are reworked. + */ +@Deprecated public class CreateChallengePanel { public CreateChallengePanel(ChallengesAddon addon, User user) { diff --git a/src/main/java/world/bentobox/challenges/panel/RequiredPanel.java b/src/main/java/world/bentobox/challenges/panel/RequiredPanel.java index 13affcb..76ff526 100644 --- a/src/main/java/world/bentobox/challenges/panel/RequiredPanel.java +++ b/src/main/java/world/bentobox/challenges/panel/RequiredPanel.java @@ -12,7 +12,7 @@ import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.ItemStack; -import world.bentobox.challenges.database.object.Challenges; +import world.bentobox.challenges.database.object.Challenge; import world.bentobox.bentobox.api.panels.Panel; import world.bentobox.bentobox.api.panels.PanelItem.ClickHandler; import world.bentobox.bentobox.api.panels.PanelListener; @@ -25,11 +25,12 @@ import world.bentobox.bentobox.util.Util; * Handles the requirements for a challenge * Items, blocks, entities * @author tastybento - * + * @deprecated All panels are reworked. */ +@Deprecated public class RequiredPanel implements ClickHandler, PanelListener { private static final int CONTROL_NUMBER = 4; - private Challenges challenge; + private Challenge challenge; private User user; private Panel panel; private Panel referringPanel; @@ -38,7 +39,7 @@ public class RequiredPanel implements ClickHandler, PanelListener { * @param challenge * @param user */ - public RequiredPanel(Challenges challenge, User user, Panel referringPanel) { + public RequiredPanel(Challenge challenge, User user, Panel referringPanel) { this.challenge = challenge; this.user = user; this.panel = openPanel(); @@ -72,7 +73,7 @@ public class RequiredPanel implements ClickHandler, PanelListener { .clickHandler(this) .build()).forEach(pb::item); return pb.user(user).build(); - case LEVEL: + case OTHER: break; default: @@ -136,8 +137,6 @@ public class RequiredPanel implements ClickHandler, PanelListener { } // Save changes switch (challenge.getChallengeType()) { - case ICON: - break; case INVENTORY: List reqItems = new ArrayList<>(); // Skip first item @@ -153,7 +152,7 @@ public class RequiredPanel implements ClickHandler, PanelListener { break; case ISLAND: break; - case LEVEL: + case OTHER: break; default: break; diff --git a/src/main/java/world/bentobox/challenges/panel/TryToComplete.java b/src/main/java/world/bentobox/challenges/panel/TryToComplete.java index 2fa22ff..3e32318 100644 --- a/src/main/java/world/bentobox/challenges/panel/TryToComplete.java +++ b/src/main/java/world/bentobox/challenges/panel/TryToComplete.java @@ -3,387 +3,624 @@ */ package world.bentobox.challenges.panel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; +import java.util.*; +import java.util.stream.Collectors; -import world.bentobox.challenges.ChallengesAddon; -import world.bentobox.challenges.ChallengesManager; -import world.bentobox.challenges.commands.ChallengesCommand; -import world.bentobox.challenges.database.object.Challenges; -import world.bentobox.challenges.database.object.Challenges.ChallengeType; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; -import world.bentobox.bentobox.hooks.VaultHook; import world.bentobox.bentobox.util.Util; -import world.bentobox.level.Level; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; +import world.bentobox.challenges.database.object.ChallengeLevel; + /** * Run when a user tries to complete a challenge * @author tastybento * */ -public class TryToComplete { +public class TryToComplete +{ +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + /** + * Challenges addon variable. + */ private ChallengesAddon addon; - private World world; - private String permPrefix; - private User user; - private ChallengesManager manager; - private Challenges challenge; - private String label; - public TryToComplete label(String label) { - this.label = label; + /** + * Challenges manager for addon. + */ + private ChallengesManager manager; + + /** + * World where all checks are necessary. + */ + private World world; + + /** + * User who is completing challenge. + */ + private User user; + + /** + * Permission prefix string. + */ + private String permissionPrefix; + + /** + * Top command first label. + */ + private String topLabel; + + /** + * Challenge that should be completed. + */ + private Challenge challenge; + + /** + * Variable that will be used to avoid multiple empty object generation. + */ + private final ChallengeResult EMPTY_RESULT = new ChallengeResult(); + +// --------------------------------------------------------------------- +// Section: Builder +// --------------------------------------------------------------------- + + @Deprecated + public TryToComplete label(String label) + { + this.topLabel = label; return this; } - public TryToComplete user(User user) { + + @Deprecated + public TryToComplete user(User user) + { this.user = user; return this; } - public TryToComplete manager(ChallengesManager manager) { + + @Deprecated + public TryToComplete manager(ChallengesManager manager) + { this.manager = manager; return this; } - public TryToComplete challenge(Challenges challenge) { + + @Deprecated + public TryToComplete challenge(Challenge challenge) + { this.challenge = challenge; return this; } - public TryToComplete world(World world) { + + @Deprecated + public TryToComplete world(World world) + { this.world = world; return this; } - public TryToComplete permPrefix(String prefix) { - this.permPrefix = prefix; + + @Deprecated + public TryToComplete permPrefix(String prefix) + { + this.permissionPrefix = prefix; return this; } - public TryToComplete(ChallengesAddon addon) { + + @Deprecated + public TryToComplete(ChallengesAddon addon) + { this.addon = addon; } - public ChallengeResult build() { + +// --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * @param addon - Challenges Addon. + * @param user - User who performs challenge. + * @param challenge - Challenge that should be completed. + * @param world - World where completion may occur. + * @param topLabel - Label of the top command. + * @param permissionPrefix - Permission prefix for GameMode addon. + */ + public TryToComplete(ChallengesAddon addon, + User user, + Challenge challenge, + World world, + String topLabel, + String permissionPrefix) + { + this.addon = addon; + this.world = world; + this.permissionPrefix = permissionPrefix; + this.user = user; + this.manager = addon.getChallengesManager(); + this.challenge = challenge; + this.topLabel = topLabel; + + this.build(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method checks if challenge can be done, and complete it, if it is possible. + * @return ChallengeResult object, that contains completion status. + */ + public ChallengeResult build() + { // Check if can complete challenge - ChallengeResult result = checkIfCanCompleteChallenge(); - if (!result.meetsRequirements) { + ChallengeResult result = this.checkIfCanCompleteChallenge(); + + if (!result.meetsRequirements) + { return result; } - if (!result.repeat) { - // Give rewards - for (ItemStack reward : challenge.getRewardItems()) { - user.getInventory().addItem(reward).forEach((k,v) -> user.getWorld().dropItem(user.getLocation(), v)); + + if (!result.repeat) + { + // Item rewards + for (ItemStack reward : this.challenge.getRewardItems()) + { + this.user.getInventory().addItem(reward).forEach((k, v) -> + this.user.getWorld().dropItem(this.user.getLocation(), v)); } - // Give money - this.addon.getPlugin().getVault().ifPresent( - vaultHook -> vaultHook.deposit(this.user, this.challenge.getRewardMoney())); + // Money Reward + if (this.addon.isEconomyProvided()) + { + this.addon.getEconomyProvider().deposit(this.user, this.challenge.getRewardMoney()); + } + + // Experience Reward + this.user.getPlayer().giveExp(this.challenge.getRewardExperience()); - // Give exp - user.getPlayer().giveExp(challenge.getRewardExp()); // Run commands - runCommands(challenge.getRewardCommands()); - user.sendMessage("challenges.you-completed", "[challenge]", challenge.getFriendlyName()); - if (addon.getConfig().getBoolean("broadcastmessages", false)) { - for (Player p : addon.getServer().getOnlinePlayers()) { + this.runCommands(this.challenge.getRewardCommands()); + + this.user.sendMessage("challenges.you-completed", "[challenge]", this.challenge.getFriendlyName()); + + if (this.addon.getChallengesSettings().isBroadcastMessages()) + { + for (Player p : this.addon.getServer().getOnlinePlayers()) + { User.getInstance(p).sendMessage("challenges.name-has-completed", - "[name]", user.getName(), "[challenge]", challenge.getFriendlyName()); + "[name]", this.user.getName(), "[challenge]", this.challenge.getFriendlyName()); } } - } else { - // Give rewards - for (ItemStack reward : challenge.getRepeatItemReward()) { - user.getInventory().addItem(reward).forEach((k,v) -> user.getWorld().dropItem(user.getLocation(), v)); + } + else + { + // Item Repeat Rewards + for (ItemStack reward : this.challenge.getRepeatItemReward()) + { + this.user.getInventory().addItem(reward).forEach((k, v) -> + this.user.getWorld().dropItem(this.user.getLocation(), v)); } - // Give money - this.addon.getPlugin().getVault().ifPresent( - vaultHook -> vaultHook.deposit(this.user, this.challenge.getRepeatMoneyReward())); + // Money Repeat Reward + if (this.addon.isEconomyProvided()) + { + this.addon.getEconomyProvider().deposit(this.user, this.challenge.getRepeatMoneyReward()); + } + + // Experience Repeat Reward + this.user.getPlayer().giveExp(this.challenge.getRepeatExperienceReward()); - // Give exp - user.getPlayer().giveExp(challenge.getRepeatExpReward()); // Run commands - runCommands(challenge.getRepeatRewardCommands()); - user.sendMessage("challenges.you-repeated", "[challenge]", challenge.getFriendlyName()); + this.runCommands(this.challenge.getRepeatRewardCommands()); + + this.user.sendMessage("challenges.you-repeated", "[challenge]", this.challenge.getFriendlyName()); } + // Mark as complete - manager.setChallengeComplete(user, challenge.getUniqueId(), world); - user.closeInventory(); - user.getPlayer().performCommand(label + " " + ChallengesCommand.CHALLENGE_COMMAND + " " + challenge.getLevel()); + this.manager.setChallengeComplete(this.user, this.challenge); + + if (!result.repeat) + { + ChallengeLevel level = this.manager.getLevel(this.challenge); + + if (!this.manager.isLevelCompleted(this.user, level)) + { + if (this.manager.validateLevelCompletion(this.user, level)) + { + // Item rewards + for (ItemStack reward : level.getRewardItems()) + { + this.user.getInventory().addItem(reward).forEach((k, v) -> + this.user.getWorld().dropItem(this.user.getLocation(), v)); + } + + // Money Reward + if (this.addon.isEconomyProvided()) + { + this.addon.getEconomyProvider().deposit(this.user, level.getRewardMoney()); + } + + // Experience Reward + this.user.getPlayer().giveExp(level.getRewardExperience()); + + // Run commands + this.runCommands(level.getRewardCommands()); + + this.user.sendMessage("challenges.you-completed-level", "[level]", level.getFriendlyName()); + + if (this.addon.getChallengesSettings().isBroadcastMessages()) + { + for (Player p : this.addon.getServer().getOnlinePlayers()) + { + User.getInstance(p).sendMessage("challenges.name-has-completed-level", + "[name]", this.user.getName(), "[level]", level.getFriendlyName()); + } + } + + this.manager.setLevelComplete(this.user, level); + } + } + } + return result; } - /** - * @param addon - * @param user - * @param manager - * @param challenge - * @param world - * @param permPrefix - */ - public TryToComplete(ChallengesAddon addon, User user, ChallengesManager manager, Challenges challenge, World world, String permPrefix, String label) { - this.addon = addon; - this.world = world; - this.permPrefix = permPrefix; - this.user = user; - this.manager = manager; - this.challenge = challenge; - - // Check if can complete challenge - ChallengeResult result = checkIfCanCompleteChallenge(); - if (!result.meetsRequirements) { - return; - } - if (!result.repeat) { - // Give rewards - for (ItemStack reward : challenge.getRewardItems()) { - user.getInventory().addItem(reward).forEach((k,v) -> user.getWorld().dropItem(user.getLocation(), v)); - } - - // Give money - this.addon.getPlugin().getVault().ifPresent( - vaultHook -> vaultHook.deposit(this.user, this.challenge.getRewardMoney())); - - // Give exp - user.getPlayer().giveExp(challenge.getRewardExp()); - // Run commands - runCommands(challenge.getRewardCommands()); - user.sendMessage("challenges.you-completed", "[challenge]", challenge.getFriendlyName()); - if (addon.getConfig().getBoolean("broadcastmessages", false)) { - for (Player p : addon.getServer().getOnlinePlayers()) { - User.getInstance(p).sendMessage("challenges.name-has-completed", - "[name]", user.getName(), "[challenge]", challenge.getFriendlyName()); - } - } - } else { - // Give rewards - for (ItemStack reward : challenge.getRepeatItemReward()) { - user.getInventory().addItem(reward).forEach((k,v) -> user.getWorld().dropItem(user.getLocation(), v)); - } - - // Give money - this.addon.getPlugin().getVault().ifPresent( - vaultHook -> vaultHook.deposit(this.user, this.challenge.getRepeatMoneyReward())); - - // Give exp - user.getPlayer().giveExp(challenge.getRepeatExpReward()); - // Run commands - runCommands(challenge.getRepeatRewardCommands()); - user.sendMessage("challenges.you-repeated", "[challenge]", challenge.getFriendlyName()); - } - // Mark as complete - manager.setChallengeComplete(user, challenge.getUniqueId(), world); - user.closeInventory(); - user.getPlayer().performCommand(label + " " + ChallengesCommand.CHALLENGE_COMMAND + " " + challenge.getLevel()); - } /** * Checks if a challenge can be completed or not + * It returns ChallengeResult. */ - private ChallengeResult checkIfCanCompleteChallenge() { + private ChallengeResult checkIfCanCompleteChallenge() + { + ChallengeResult result; + + ChallengeType type = this.challenge.getChallengeType(); + // Check the world - if (!Util.getWorld(user.getWorld()).getName().equalsIgnoreCase(challenge.getWorld())) { - user.sendMessage("general.errors.wrong-world"); - return new ChallengeResult(); + if (!this.challenge.isDeployed()) + { + this.user.sendMessage("challenges.error.not-deployed"); + result = EMPTY_RESULT; } - // Check if user has the - if (!challenge.getLevel().equals(ChallengesManager.FREE) && !manager.isLevelUnlocked(user, challenge.getLevel(), world)) { - user.sendMessage("challenges.errors.challenge-level-not-available"); - return new ChallengeResult(); + else if (Util.getWorld(this.world) != Util.getWorld(this.user.getWorld()) || + !this.challenge.getUniqueId().startsWith(Util.getWorld(this.world).getName())) + { + this.user.sendMessage("general.errors.wrong-world"); + result = EMPTY_RESULT; + } + // Player is not on island + else if (!this.addon.getIslands().userIsOnIsland(this.user.getWorld(), this.user)) + { + this.user.sendMessage("challenges.error.not-on-island"); + result = EMPTY_RESULT; + } + // Check if user has unlocked challenges level. + else if (!this.challenge.getLevel().equals(ChallengesManager.FREE) && + !this.manager.isLevelUnlocked(this.user, this.world, this.manager.getLevel(this.challenge.getLevel()))) + { + this.user.sendMessage("challenges.errors.challenge-level-not-available"); + result = EMPTY_RESULT; } // Check max times - if (challenge.isRepeatable() && challenge.getMaxTimes() > 0 && manager.checkChallengeTimes(user, challenge, world) >= challenge.getMaxTimes()) { - user.sendMessage("challenges.not-repeatable"); - return new ChallengeResult(); + else if (this.challenge.isRepeatable() && this.challenge.getMaxTimes() > 0 && + this.manager.getChallengeTimes(this.user, this.challenge) >= this.challenge.getMaxTimes()) + { + this.user.sendMessage("challenges.not-repeatable"); + result = EMPTY_RESULT; } // Check repeatability - if (manager.isChallengeComplete(user, challenge.getUniqueId(), world) - && (!challenge.isRepeatable() || challenge.getChallengeType().equals(ChallengeType.LEVEL) - || challenge.getChallengeType().equals(ChallengeType.ISLAND))) { - user.sendMessage("challenges.not-repeatable"); - return new ChallengeResult(); - } - - // Check money - Optional vaultHook = this.addon.getPlugin().getVault(); - - if (vaultHook.isPresent()) + else if (!this.challenge.isRepeatable() && this.manager.isChallengeComplete(this.user, this.challenge)) { - if (!vaultHook.get().has(this.user, this.challenge.getReqMoney())) - { - this.user.sendMessage("challenges.not-enough-money", "[money]", Integer.toString(this.challenge.getReqMoney())); - return new ChallengeResult(); - } + this.user.sendMessage("challenges.not-repeatable"); + result = EMPTY_RESULT; } - - // Check exp - if (this.user.getPlayer().getTotalExperience() < this.challenge.getReqExp()) + // Check environment + else if (!this.challenge.getEnvironment().isEmpty() && + !this.challenge.getEnvironment().contains(this.user.getWorld().getEnvironment())) { - this.user.sendMessage("challenges.not-enough-exp", "[xp]", Integer.toString(this.challenge.getReqExp())); - return new ChallengeResult(); + this.user.sendMessage("challenges.errors.wrong-environment"); + result = EMPTY_RESULT; + } + // Check permission + else if (!this.checkPermissions()) + { + this.user.sendMessage("general.errors.no-permission"); + result = EMPTY_RESULT; + } + else if (type.equals(ChallengeType.INVENTORY)) + { + result = this.checkInventory(); + } + else if (type.equals(ChallengeType.ISLAND)) + { + result = this.checkSurrounding(); + } + else if (type.equals(ChallengeType.OTHER)) + { + result = this.checkOthers(); + } + else + { + result = EMPTY_RESULT; } - switch (challenge.getChallengeType()) { - case INVENTORY: - return checkInventory(); - case LEVEL: - return checkLevel(); - case ISLAND: - return checkSurrounding(); - default: - return new ChallengeResult(); - } - } - - private ChallengeResult checkInventory() { - // Run through inventory - List required = new ArrayList<>(challenge.getRequiredItems()); - for (ItemStack req : required) { - // Check for FIREWORK_ROCKET, ENCHANTED_BOOK, WRITTEN_BOOK, POTION and FILLED_MAP because these have unique meta when created - switch (req.getType()) { - case FIREWORK_ROCKET: - case ENCHANTED_BOOK: - case WRITTEN_BOOK: - case FILLED_MAP: - // Get how many items are in the inventory. Item stacks amounts need to be summed - int numInInventory = Arrays.stream(user.getInventory().getContents()).filter(Objects::nonNull).filter(i -> i.getType().equals(req.getType())).mapToInt(i -> i.getAmount()).sum(); - if (numInInventory < req.getAmount()) { - user.sendMessage("challenges.error.not-enough-items", "[items]", Util.prettifyText(req.getType().toString())); - return new ChallengeResult(); - } - break; - default: - // General checking - if (!user.getInventory().containsAtLeast(req, req.getAmount())) { - user.sendMessage("challenges.error.not-enough-items", "[items]", Util.prettifyText(req.getType().toString())); - return new ChallengeResult(); - } - } - - } - // If remove items, then remove them - if (challenge.isTakeItems()) { - removeItems(required); - - } - - // process money removal - this.removeMoney(); - - // Return the result - return new ChallengeResult().setMeetsRequirements().setRepeat(manager.isChallengeComplete(user, challenge.getUniqueId(), world)); + // Everything fails till this point. + return result; } /** - * This method withdraw user money, if challenge Required Money is larger then 0, and - * it is set to removal. - * This works only if vaultHook is enabled. - */ - private void removeMoney() + * This method checks if user has all required permissions. + * @return true if user has all required permissions, otherwise false. + */ + private boolean checkPermissions() { - Optional vaultHook = this.addon.getPlugin().getVault(); + return this.challenge.getRequiredPermissions().isEmpty() || + this.challenge.getRequiredPermissions().stream().allMatch(s -> this.user.hasPermission(s)); + } - if (vaultHook.isPresent() && - this.challenge.isTakeMoney() && - this.challenge.getReqMoney() > 0) + /** + * This method runs all commands from command list. + * @param commands List of commands that must be performed. + */ + private void runCommands(List commands) + { + // Ignore commands with this perm + if (user.hasPermission(this.permissionPrefix + "command.challengeexempt") && !user.isOp()) { - vaultHook.get().withdraw(this.user, this.challenge.getReqMoney()); + return; } + for (String cmd : commands) + { + if (cmd.startsWith("[SELF]")) + { + String alert = "Running command '" + cmd + "' as " + this.user.getName(); + this.addon.getLogger().info(alert); + cmd = cmd.substring(6, cmd.length()).replace("[player]", this.user.getName()).trim(); + try + { + if (!user.performCommand(cmd)) + { + this.showError(cmd); + } + } + catch (Exception e) + { + this.showError(cmd); + } + + continue; + } + // Substitute in any references to player + try + { + if (!this.addon.getServer().dispatchCommand(this.addon.getServer().getConsoleSender(), + cmd.replace("[player]", this.user.getName()))) + { + this.showError(cmd); + } + } + catch (Exception e) + { + this.showError(cmd); + } + } + } + + + /** + * Throws error message. + * @param cmd Error message that appear after failing some command. + */ + private void showError(final String cmd) + { + this.addon.getLogger().severe("Problem executing command executed by player - skipping!"); + this.addon.getLogger().severe(() -> "Command was : " + cmd); + } + + +// --------------------------------------------------------------------- +// Section: Inventory Challenge +// --------------------------------------------------------------------- + + + /** + * Checks if a inventory challenge can be completed or not + * It returns ChallengeResult. + */ + private ChallengeResult checkInventory() + { + // Run through inventory + List required = new ArrayList<>(this.challenge.getRequiredItems()); + for (ItemStack req : required) + { + // Check for FIREWORK_ROCKET, ENCHANTED_BOOK, WRITTEN_BOOK, POTION and FILLED_MAP because these have unique meta when created + switch (req.getType()) + { + case FIREWORK_ROCKET: + case ENCHANTED_BOOK: + case WRITTEN_BOOK: + case FILLED_MAP: + // Get how many items are in the inventory. Item stacks amounts need to be summed + int numInInventory = + Arrays.stream(this.user.getInventory().getContents()).filter(Objects::nonNull). + filter(i -> i.getType().equals(req.getType())). + mapToInt(ItemStack::getAmount). + sum(); + + if (numInInventory < req.getAmount()) + { + this.user.sendMessage("challenges.error.not-enough-items", + "[items]", + Util.prettifyText(req.getType().toString())); + return EMPTY_RESULT; + } + break; + default: + // General checking + if (!this.user.getInventory().containsAtLeast(req, req.getAmount())) + { + this.user.sendMessage("challenges.error.not-enough-items", + "[items]", + Util.prettifyText(req.getType().toString())); + return EMPTY_RESULT; + } + } + } + + // If remove items, then remove them + + if (this.challenge.isTakeItems()) + { + this.removeItems(required); + } + + + // Return the result + return new ChallengeResult().setMeetsRequirements().setRepeat( + this.manager.isChallengeComplete(this.user, this.challenge)); } /** * Removes items from a user's inventory * @param required - a list of item stacks to be removed - * @return Map of item type and quantity that were successfully removed from the user's inventory */ - public Map removeItems(List required) { + Map removeItems(List required) + { Map removed = new HashMap<>(); - for (ItemStack req : required) { + + for (ItemStack req : required) + { int amountToBeRemoved = req.getAmount(); - List itemsInInv = Arrays.stream(user.getInventory().getContents()).filter(Objects::nonNull).filter(i -> i.getType().equals(req.getType())).collect(Collectors.toList()); - for (ItemStack i : itemsInInv) { - if (amountToBeRemoved > 0) { + List itemsInInv = Arrays.stream(user.getInventory().getContents()). + filter(Objects::nonNull). + filter(i -> i.getType().equals(req.getType())). + collect(Collectors.toList()); + + for (ItemStack i : itemsInInv) + { + if (amountToBeRemoved > 0) + { // Remove either the full amount or the remaining amount - if (i.getAmount() >= amountToBeRemoved) { + if (i.getAmount() >= amountToBeRemoved) + { i.setAmount(i.getAmount() - amountToBeRemoved); removed.merge(i.getType(), amountToBeRemoved, Integer::sum); amountToBeRemoved = 0; - } else { + } + else + { removed.merge(i.getType(), i.getAmount(), Integer::sum); amountToBeRemoved -= i.getAmount(); i.setAmount(0); - } } } - if (amountToBeRemoved > 0) { - addon.logError("Could not remove " + amountToBeRemoved + " of " + req.getType() + " from player's inventory!"); + + if (amountToBeRemoved > 0) + { + this.addon.logError("Could not remove " + amountToBeRemoved + " of " + req.getType() + + " from player's inventory!"); } } + return removed; } - private ChallengeResult checkLevel() { - // Check if the level addon is installed or not - long level = addon.getAddonByName("Level") - .map(l -> ((Level)l).getIslandLevel(world, user.getUniqueId())).orElse(0L); - if (level >= challenge.getReqIslandlevel()) { - // process money removal - this.removeMoney(); - return new ChallengeResult().setMeetsRequirements(); - } else { - user.sendMessage("challenges.error.island-level", TextVariables.NUMBER, String.valueOf(challenge.getReqIslandlevel())); - return new ChallengeResult(); - } - } - private ChallengeResult checkSurrounding() { - if (!addon.getIslands().userIsOnIsland(world, user)) { - // Player is not on island - user.sendMessage("challenges.error.not-on-island"); - return new ChallengeResult(); - } - // Check for items or entities in the area - ChallengeResult result = searchForEntities(challenge.getRequiredEntities(), challenge.getSearchRadius()); - if (result.meetsRequirements && !challenge.getRequiredBlocks().isEmpty()) { - // Search for items only if entities found - result = searchForBlocks(challenge.getRequiredBlocks(), challenge.getSearchRadius()); - } +// --------------------------------------------------------------------- +// Section: Island Challenge +// --------------------------------------------------------------------- - if (result.meetsRequirements && this.challenge.isTakeMoney()) + + /** + * Checks if a island challenge can be completed or not + * It returns ChallengeResult. + */ + private ChallengeResult checkSurrounding() + { + ChallengeResult result; + + if (!this.addon.getIslands().userIsOnIsland(this.user.getWorld(), this.user)) { - // process money removal - this.removeMoney(); + // Player is not on island + this.user.sendMessage("challenges.error.not-on-island"); + result = EMPTY_RESULT; + } + else + { + // Check for items or entities in the area + + result = this.searchForEntities(this.challenge.getRequiredEntities(), this.challenge.getSearchRadius()); + + if (result.meetsRequirements && !this.challenge.getRequiredBlocks().isEmpty()) + { + // Search for items only if entities found + result = this.searchForBlocks(this.challenge.getRequiredBlocks(), this.challenge.getSearchRadius()); + } + + if (result.meetsRequirements && + this.challenge.isRemoveEntities() && + !this.challenge.getRequiredEntities().isEmpty()) + { + this.removeEntities(); + } + + if (result.meetsRequirements && + this.challenge.isRemoveBlocks() && + !this.challenge.getRequiredBlocks().isEmpty()) + { + this.removeBlocks(); + } } return result; } - private ChallengeResult searchForBlocks(Map map, int searchRadius) { + + /** + * This method search required blocks in given radius from user position. + * @param map RequiredBlock Map. + * @param searchRadius Search distance + * @return ChallengeResult + */ + private ChallengeResult searchForBlocks(Map map, int searchRadius) + { 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++) { - Material mat = user.getWorld().getBlockAt(user.getLocation().add(new Vector(x,y,z))).getType(); + + for (int x = -searchRadius; x <= searchRadius; x++) + { + for (int y = -searchRadius; y <= searchRadius; y++) + { + for (int z = -searchRadius; z <= searchRadius; z++) + { + Material mat = this.user.getWorld().getBlockAt(this.user.getLocation().add(new Vector(x, y, z))).getType(); // Remove one blocks.computeIfPresent(mat, (b, amount) -> amount - 1); // Remove any that have an amount of 0 @@ -391,93 +628,189 @@ public class TryToComplete { } } } - if (blocks.isEmpty()) { + + if (blocks.isEmpty()) + { return new ChallengeResult().setMeetsRequirements(); } - user.sendMessage("challenges.error.not-close-enough", "[number]", String.valueOf(searchRadius)); - blocks.forEach((k,v) -> user.sendMessage("challenges.error.you-still-need", - "[amount]", String.valueOf(v), - "[item]", Util.prettifyText(k.toString()))); - return new ChallengeResult(); - } + this.user.sendMessage("challenges.error.not-close-enough", "[number]", String.valueOf(searchRadius)); - private ChallengeResult searchForEntities(Map map, int searchRadius) { - Map entities = map.isEmpty() ? new EnumMap<>(EntityType.class) : 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); - entities.entrySet().removeIf(e -> e.getValue() == 0); - }); - if (entities.isEmpty()) { - return new ChallengeResult().setMeetsRequirements(); - } - entities.forEach((reqEnt, amount) -> user.sendMessage("challenges.error.you-still-need", - "[amount]", String.valueOf(amount), - "[item]", Util.prettifyText(reqEnt.toString()))); - return new ChallengeResult(); + blocks.forEach((k, v) -> user.sendMessage("challenges.error.you-still-need", + "[amount]", String.valueOf(v), + "[item]", Util.prettifyText(k.toString()))); + + return EMPTY_RESULT; } /** - * Contains flags on completion of challenge - * @author tastybento - * + * This method search required entities in given radius from user position. + * @param map RequiredEntities Map. + * @param searchRadius Search distance + * @return ChallengeResult */ - public class ChallengeResult { + private ChallengeResult searchForEntities(Map map, int searchRadius) + { + Map entities = map.isEmpty() ? new EnumMap<>(EntityType.class) : new EnumMap<>(map); + + this.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); + entities.entrySet().removeIf(e -> e.getValue() == 0); + }); + + if (entities.isEmpty()) + { + return new ChallengeResult().setMeetsRequirements(); + } + + entities.forEach((reqEnt, amount) -> this.user.sendMessage("challenges.error.you-still-need", + "[amount]", String.valueOf(amount), + "[item]", Util.prettifyText(reqEnt.toString()))); + + return EMPTY_RESULT; + } + + + /** + * This method removes required block and set air instead of it. + */ + private void removeBlocks() + { + Map blocks = new EnumMap<>(this.challenge.getRequiredBlocks()); + int searchRadius = this.challenge.getSearchRadius(); + + for (int x = -searchRadius; x <= searchRadius; x++) + { + for (int y = -searchRadius; y <= searchRadius; y++) + { + for (int z = -searchRadius; z <= searchRadius; z++) + { + Block block = this.user.getWorld().getBlockAt(this.user.getLocation().add(new Vector(x, y, z))); + + if (blocks.containsKey(block.getType())) + { + blocks.computeIfPresent(block.getType(), (b, amount) -> amount - 1); + blocks.entrySet().removeIf(en -> en.getValue() <= 0); + + block.setType(Material.AIR); + } + } + } + } + } + + + /** + * This method removes required entities. + */ + private void removeEntities() + { + Map entities = this.challenge.getRequiredEntities().isEmpty() ? + new EnumMap<>(EntityType.class) : new EnumMap<>(this.challenge.getRequiredEntities()); + + int searchRadius = this.challenge.getSearchRadius(); + + this.user.getPlayer().getNearbyEntities(searchRadius, searchRadius, searchRadius).forEach(entity -> { + // Look through all the nearby Entities, filtering by type + + if (entities.containsKey(entity.getType())) + { + entities.computeIfPresent(entity.getType(), (reqEntity, amount) -> amount - 1); + entities.entrySet().removeIf(e -> e.getValue() == 0); + entity.remove(); + } + }); + } + + +// --------------------------------------------------------------------- +// Section: Other challenge +// --------------------------------------------------------------------- + + + /** + * Checks if a other challenge can be completed or not + * It returns ChallengeResult. + */ + private ChallengeResult checkOthers() + { + if (!this.addon.isEconomyProvided() || + this.challenge.getRequiredMoney() <= 0 || + !this.addon.getEconomyProvider().has(this.user, this.challenge.getRequiredMoney())) + { + this.user.sendMessage("challenges.not-enough-money", + "[money]", + Integer.toString(this.challenge.getRequiredMoney())); + } + else if (this.challenge.getRequiredExperience() <= 0 || + this.user.getPlayer().getTotalExperience() < this.challenge.getRequiredExperience()) + { + this.user.sendMessage("challenges.not-enough-exp", + "[xp]", + Integer.toString(this.challenge.getRequiredExperience())); + } + else if (!this.addon.isLevelProvided() || + this.addon.getLevelAddon().getIslandLevel(this.world, this.user.getUniqueId()) < this.challenge.getRequiredIslandLevel()) + { + this.user.sendMessage("challenges.error.island-level", + TextVariables.NUMBER, + String.valueOf(this.challenge.getRequiredIslandLevel())); + } + else + { + if (this.addon.isEconomyProvided() && this.challenge.isTakeMoney()) + { + this.addon.getEconomyProvider().withdraw(this.user, this.challenge.getRequiredMoney()); + } + + if (this.challenge.isTakeExperience()) + { + this.user.getPlayer().setTotalExperience( + this.user.getPlayer().getTotalExperience() - this.challenge.getRequiredExperience()); + } + + return new ChallengeResult().setMeetsRequirements(); + } + + return EMPTY_RESULT; + } + + +// --------------------------------------------------------------------- +// Section: Private classes +// --------------------------------------------------------------------- + + + /** + * Contains flags on completion of challenge + * + * @author tastybento + */ + private class ChallengeResult + { private boolean meetsRequirements; + private boolean repeat; + + /** - * @param meetsRequirements the meetsRequirements to set */ - public ChallengeResult setMeetsRequirements() { + ChallengeResult setMeetsRequirements() + { this.meetsRequirements = true; return this; } + + /** * @param repeat the repeat to set */ - public ChallengeResult setRepeat(boolean repeat) { + ChallengeResult setRepeat(boolean repeat) + { this.repeat = repeat; return this; } - - } - - private void runCommands(List commands) { - // Ignore commands with this perm - if (user.hasPermission(permPrefix + "command.challengeexempt") && !user.isOp()) { - return; - } - for (String cmd : commands) { - if (cmd.startsWith("[SELF]")) { - String alert = "Running command '" + cmd + "' as " + user.getName(); - addon.getLogger().info(alert); - cmd = cmd.substring(6,cmd.length()).replace("[player]", user.getName()).trim(); - try { - if (!user.performCommand(cmd)) { - showError(cmd); - } - } catch (Exception e) { - showError(cmd); - } - - continue; - } - // Substitute in any references to player - try { - if (!addon.getServer().dispatchCommand(addon.getServer().getConsoleSender(), cmd.replace("[player]", user.getName()))) { - showError(cmd); - } - } catch (Exception e) { - 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/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java new file mode 100644 index 0000000..644e0d0 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/AdminGUI.java @@ -0,0 +1,414 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import java.util.Collections; +import java.util.List; + +import net.wesjd.anvilgui.AnvilGUI; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class contains Main + */ +public class AdminGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This boolean holds if import should overwrite existing challenges. + */ + private boolean overwriteMode; + + /** + * This indicate if Reset Challenges must work as reset all. + */ + private boolean resetAllMode; + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * This enum contains all button variations. Just for cleaner code. + */ + private enum Button + { + COMPLETE_USER_CHALLENGES, + RESET_USER_CHALLENGES, + ADD_CHALLENGE, + ADD_LEVEL, + EDIT_CHALLENGE, + EDIT_LEVEL, + DELETE_CHALLENGE, + DELETE_LEVEL, + IMPORT_CHALLENGES, + EDIT_SETTINGS + } + + +// --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + public AdminGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, world, user, topLabel, permissionPrefix); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.admin.gui-title")); + + GuiUtils.fillBorder(panelBuilder); + + panelBuilder.item(10, this.createButton(Button.COMPLETE_USER_CHALLENGES)); + panelBuilder.item(19, this.createButton(Button.RESET_USER_CHALLENGES)); + + // Add Challenges + panelBuilder.item(12, this.createButton(Button.ADD_CHALLENGE)); + panelBuilder.item(13, this.createButton(Button.ADD_LEVEL)); + + // Edit Challenges + panelBuilder.item(21, this.createButton(Button.EDIT_CHALLENGE)); + panelBuilder.item(22, this.createButton(Button.EDIT_LEVEL)); + + // Remove Challenges + panelBuilder.item(30, this.createButton(Button.DELETE_CHALLENGE)); + panelBuilder.item(31, this.createButton(Button.DELETE_LEVEL)); + + + // Import Challenges + panelBuilder.item(15, this.createButton(Button.IMPORT_CHALLENGES)); + + // Edit Addon Settings + panelBuilder.item(16, this.createButton(Button.EDIT_SETTINGS)); + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method is used to create PanelItem for each button type. + * @param button Button which must be created. + * @return PanelItem with necessary functionality. + */ + private PanelItem createButton(Button button) + { + ItemStack icon; + String name; + List description; + boolean glow; + PanelItem.ClickHandler clickHandler; + + String permissionSuffix; + + switch (button) + { + case COMPLETE_USER_CHALLENGES: + permissionSuffix = COMPLETE; + + name = this.user.getTranslation("challenges.gui.admin.buttons.complete"); + description = Collections.emptyList(); + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new ListUsersGUI(this.addon, + this.world, + this.user, + ListUsersGUI.Mode.COMPLETE, + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + + break; + case RESET_USER_CHALLENGES: + permissionSuffix = RESET; + + name = this.user.getTranslation("challenges.gui.admin.buttons.reset"); + description = Collections.emptyList(); + icon = new ItemStack(Material.WRITABLE_BOOK); + + glow = this.resetAllMode; + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.resetAllMode = !this.resetAllMode; + this.build(); + } + else + { + new ListUsersGUI(this.addon, + this.world, + this.user, + this.resetAllMode ? ListUsersGUI.Mode.RESET_ALL : ListUsersGUI.Mode.RESET, + this.topLabel, + this.permissionPrefix, + this).build(); + } + + return true; + }; + + break; + case ADD_CHALLENGE: + permissionSuffix = ADD; + + name = this.user.getTranslation("challenges.gui.admin.buttons.add-challenge"); + description = Collections.emptyList(); + icon = new ItemStack(Material.BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + "unique_id", + (player, reply) -> { + String newName = Util.getWorld(this.world).getName() + "_" + reply; + + if (!this.addon.getChallengesManager().containsChallenge(newName)) + { + new EditChallengeGUI(this.addon, + this.world, + this.user, + this.addon.getChallengesManager().createChallenge(newName), + this.topLabel, + this.permissionPrefix, + this).build(); + } + else + { + this.user.sendMessage("challenges.errors.unique-id", "[id]", reply); + } + + return reply; + }); + + return true; + }; + glow = false; + + break; + case ADD_LEVEL: + permissionSuffix = ADD; + + name = this.user.getTranslation("challenges.gui.admin.buttons.add-level"); + description = Collections.emptyList(); + icon = new ItemStack(Material.BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + "unique_id", + (player, reply) -> { + String newName = Util.getWorld(this.world).getName() + "_" + reply; + + if (!this.addon.getChallengesManager().containsLevel(newName)) + { + new EditLevelGUI(this.addon, + this.world, + this.user, + this.addon.getChallengesManager().createLevel(newName), + this.topLabel, + this.permissionPrefix, + this).build(); + } + else + { + this.user.sendMessage("challenges.errors.unique-id", "[id]", reply); + } + + return reply; + }); + + return true; + }; + glow = false; + + break; + case EDIT_CHALLENGE: + permissionSuffix = EDIT; + + name = this.user.getTranslation("challenges.gui.admin.buttons.edit-challenge"); + description = Collections.emptyList(); + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + new ListChallengesGUI(this.addon, + this.world, + this.user, + ListChallengesGUI.Mode.EDIT, + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + + break; + case EDIT_LEVEL: + { + permissionSuffix = EDIT; + + name = this.user.getTranslation("challenges.gui.admin.buttons.edit-level"); + description = Collections.emptyList(); + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + new ListLevelsGUI(this.addon, + this.world, + this.user, + ListLevelsGUI.Mode.EDIT, + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + + break; + } + case DELETE_CHALLENGE: + { + permissionSuffix = DELETE; + + name = this.user.getTranslation("challenges.gui.admin.buttons.delete-challenge"); + description = Collections.emptyList(); + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + new ListChallengesGUI(this.addon, + this.world, + this.user, + ListChallengesGUI.Mode.DELETE, + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + + break; + } + case DELETE_LEVEL: + { + permissionSuffix = DELETE; + + name = this.user.getTranslation("challenges.gui.admin.buttons.delete-level"); + description = Collections.emptyList(); + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + new ListLevelsGUI(this.addon, + this.world, + this.user, + ListLevelsGUI.Mode.DELETE, + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + + break; + } + case IMPORT_CHALLENGES: + { + permissionSuffix = IMPORT; + + name = this.user.getTranslation("challenges.gui.admin.buttons.import"); + description = Collections.emptyList(); + icon = new ItemStack(Material.HOPPER); + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.overwriteMode = !this.overwriteMode; + this.build(); + } + else + { + // Run import command. + this.user.performCommand(this.topLabel + " " + CHALLENGES + " " + IMPORT + + (this.overwriteMode ? " overwrite" : "")); + } + return true; + }; + glow = this.overwriteMode; + + break; + } + case EDIT_SETTINGS: + { + permissionSuffix = SETTINGS; + + name = this.user.getTranslation("challenges.gui.admin.buttons.settings"); + description = Collections.emptyList(); + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + new EditSettingsGUI(this.addon, + this.world, + this.user, + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + + break; + } + default: + // This should never happen. + return null; + } + + // If user does not have permission to run command, then change icon and clickHandler. + final String actionPermission = this.permissionPrefix + ADMIN + "." + CHALLENGES + "." + permissionSuffix; + + if (!this.user.hasPermission(actionPermission)) + { + icon = new ItemStack(Material.BARRIER); + clickHandler = (panel, user, clickType, slot) -> { + this.user.sendMessage("general.errors.no-permission", "[permission]", actionPermission); + return true; + }; + } + + return new PanelItem(icon, name, description, glow, clickHandler, false); + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java new file mode 100644 index 0000000..812b58f --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditChallengeGUI.java @@ -0,0 +1,1292 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.*; + +import net.wesjd.anvilgui.AnvilGUI; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.ItemSwitchGUI; +import world.bentobox.challenges.panel.util.NumberGUI; +import world.bentobox.challenges.panel.util.SelectEnvironmentGUI; +import world.bentobox.challenges.panel.util.StringListGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class contains all necessary methods that creates GUI and allow to edit challenges + * properties. + * TODO: ISLAND is not repeatable. + */ +public class EditChallengeGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + * @param challenge challenge that needs editing. + */ + public EditChallengeGUI(ChallengesAddon addon, + World world, + User user, + Challenge challenge, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, challenge, topLabel, permissionPrefix, null); + } + + + /** + * {@inheritDoc} + * @param challenge challenge that needs editing. + */ + public EditChallengeGUI(ChallengesAddon addon, + World world, + User user, + Challenge challenge, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + this.challenge = challenge; + + // Default panel should be Properties. + this.currentMenuType = MenuType.PROPERTIES; + } + + +// --------------------------------------------------------------------- +// Section: Panel Creation related methods +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.gui.admin.edit-challenge-title")); + + GuiUtils.fillBorder(panelBuilder); + + panelBuilder.item(2, this.createMenuButton(MenuType.PROPERTIES)); + panelBuilder.item(4, this.createMenuButton(MenuType.REQUIREMENTS)); + panelBuilder.item(6, this.createMenuButton(MenuType.REWARDS)); + + if (this.currentMenuType.equals(MenuType.PROPERTIES)) + { + this.buildMainPropertiesPanel(panelBuilder); + } + else if (this.currentMenuType.equals(MenuType.REQUIREMENTS)) + { + switch (this.challenge.getChallengeType()) + { + case INVENTORY: + this.buildInventoryRequirementsPanel(panelBuilder); + break; + case ISLAND: + this.buildIslandRequirementsPanel(panelBuilder); + break; + case OTHER: + this.buildOtherRequirementsPanel(panelBuilder); + break; + } + } + else if (this.currentMenuType.equals(MenuType.REWARDS)) + { + this.buildRewardsPanel(panelBuilder); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This class populate ChallengesEditGUI with main challenge settings. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildMainPropertiesPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createButton(Button.NAME)); + panelBuilder.item(13, this.createButton(Button.TYPE)); + panelBuilder.item(16, this.createButton(Button.DEPLOYED)); + + panelBuilder.item(19, this.createButton(Button.ICON)); + panelBuilder.item(22, this.createButton(Button.DESCRIPTION)); + panelBuilder.item(25, this.createButton(Button.ORDER)); + + panelBuilder.item(28, this.createButton(Button.ENVIRONMENT)); + panelBuilder.item(31, this.createButton(Button.REMOVE_ON_COMPLETE)); + } + + + /** + * This class populates ChallengesEditGUI with island challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildIslandRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(19, this.createButton(Button.REQUIRED_ENTITIES)); + panelBuilder.item(28, this.createButton(Button.REMOVE_ENTITIES)); + + panelBuilder.item(21, this.createButton(Button.REQUIRED_BLOCKS)); + panelBuilder.item(30, this.createButton(Button.REMOVE_BLOCKS)); + + panelBuilder.item(23, this.createButton(Button.SEARCH_RADIUS)); + panelBuilder.item(25, this.createButton(Button.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with inventory challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildInventoryRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createButton(Button.REQUIRED_ITEMS)); + panelBuilder.item(19, this.createButton(Button.REMOVE_ITEMS)); + + panelBuilder.item(25, this.createButton(Button.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with other challenges requirement elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildOtherRequirementsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createButton(Button.REQUIRED_EXPERIENCE)); + panelBuilder.item(19, this.createButton(Button.REMOVE_EXPERIENCE)); + + panelBuilder.item(12, this.createButton(Button.REQUIRED_MONEY)); + panelBuilder.item(21, this.createButton(Button.REMOVE_MONEY)); + + panelBuilder.item(23, this.createButton(Button.REQUIRED_LEVEL)); + + panelBuilder.item(25, this.createButton(Button.REQUIRED_PERMISSIONS)); + } + + + /** + * This class populates ChallengesEditGUI with challenges reward elements. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildRewardsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createButton(Button.REWARD_TEXT)); + panelBuilder.item(19, this.createButton(Button.REWARD_COMMANDS)); + + panelBuilder.item(11, this.createButton(Button.REWARD_ITEM)); + panelBuilder.item(20, this.createButton(Button.REWARD_EXPERIENCE)); + panelBuilder.item(29, this.createButton(Button.REWARD_MONEY)); + + panelBuilder.item(22, this.createButton(Button.REPEATABLE)); + + if (this.challenge.isRepeatable()) + { + panelBuilder.item(31, this.createButton(Button.REPEAT_COUNT)); + + panelBuilder.item(15, this.createButton(Button.REPEAT_REWARD_TEXT)); + panelBuilder.item(24, this.createButton(Button.REPEAT_REWARD_COMMANDS)); + + panelBuilder.item(16, this.createButton(Button.REPEAT_REWARD_ITEM)); + panelBuilder.item(25, this.createButton(Button.REPEAT_REWARD_EXPERIENCE)); + panelBuilder.item(34, this.createButton(Button.REPEAT_REWARD_MONEY)); + } + } + + +// --------------------------------------------------------------------- +// Section: Other methods +// --------------------------------------------------------------------- + + + /** + * This method creates top menu buttons, that allows to switch "tabs". + * @param menuType Menu Type which button must be constructed. + * @return PanelItem that represents given menu type. + */ + private PanelItem createMenuButton(MenuType menuType) + { + ItemStack icon; + String name; + List description; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (menuType) + { + case PROPERTIES: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.properties"); + description = Collections.emptyList(); + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.PROPERTIES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.PROPERTIES); + break; + } + case REQUIREMENTS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.requirements"); + description = Collections.emptyList(); + icon = new ItemStack(Material.HOPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REQUIREMENTS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REQUIREMENTS); + break; + } + case REWARDS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.rewards"); + description = Collections.emptyList(); + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REWARDS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REWARDS); + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), glow, clickHandler, false); + } + + + /** + * This method creates buttons for default main menu. + * @param button Button which panel item must be created. + * @return PanelItem that represetns given button. + */ + private PanelItem createButton(Button button) + { + ItemStack icon; + String name; + List description; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case TYPE: + { + List values = new ArrayList<>(Challenge.ChallengeType.values().length); + + for (Challenge.ChallengeType type : Challenge.ChallengeType.values()) + { + values.add((this.challenge.getChallengeType().equals(type) ? "&2" : "&c") + + this.user.getTranslation("challenges.gui.admin.descriptions." + type.name().toLowerCase())); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.type", + "[value]", this.challenge.getChallengeType().name()); + description = values; + + if (this.challenge.getChallengeType().equals(Challenge.ChallengeType.ISLAND)) + { + icon = new ItemStack(Material.GRASS_BLOCK); + } + else if (this.challenge.getChallengeType().equals(Challenge.ChallengeType.INVENTORY)) + { + icon = new ItemStack(Material.CHEST); + } + else if (this.challenge.getChallengeType().equals(Challenge.ChallengeType.OTHER)) + { + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + } + else + { + icon = this.challenge.getIcon(); + } + + clickHandler = (panel, user, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.challenge.setChallengeType( + this.getPreviousType(this.challenge.getChallengeType())); + } + else + { + this.challenge.setChallengeType( + this.getNextType(this.challenge.getChallengeType())); + } + + this.build(); + + return true; + }; + glow = false; + break; + } + case DEPLOYED: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.deployed"); + + if (this.challenge.isDeployed()) + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setDeployed(!this.challenge.isDeployed()); + + this.build(); + return true; + }; + glow = this.challenge.isDeployed(); + break; + } + case ICON: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.icon"); + description = Collections.emptyList(); + icon = this.challenge.getIcon(); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challenge.getIcon().getType().name(), + (player, reply) -> { + Material material = Material.getMaterial(reply); + + if (material != null) + { + this.challenge.setIcon(new ItemStack(material)); + this.build(); + } + else + { + this.user.sendMessage("challenges.errors.wrong-icon", "[value]", reply); + } + + return reply; + }); + + return true; + }; + glow = false; + break; + } + case DESCRIPTION: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.description"); + description = Collections.emptyList(); + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new StringListGUI(this.user, this.challenge.getDescription(), (status, value) -> { + if (status) + { + this.challenge.setDescription(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case ORDER: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.order"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.order", + "[value]", + Integer.toString(this.challenge.getOrder()))); + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getOrder(), -1, 54, (status, value) -> { + if (status) + { + this.challenge.setOrder(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case ENVIRONMENT: + { + List values = new ArrayList<>(World.Environment.values().length); + + for (World.Environment environment : World.Environment.values()) + { + values.add((this.challenge.getEnvironment().contains(environment.name()) ? "&2" : "&c") + + this.user.getTranslation("challenges.gui.admin.descriptions." + environment.name().toLowerCase())); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.environment"); + description = values; + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + new SelectEnvironmentGUI(this.user, this.challenge.getEnvironment(), (status, value) -> { + if (status) + { + this.challenge.setEnvironment(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REMOVE_ON_COMPLETE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-on-complete"); + description = Collections.emptyList(); + + if (this.challenge.isRemoveWhenCompleted()) + { + icon = new ItemStack(Material.LAVA_BUCKET); + } + else + { + icon = new ItemStack(Material.BUCKET); + } + + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setRemoveWhenCompleted(!this.challenge.isRemoveWhenCompleted()); + this.build(); + + return true; + }; + glow = this.challenge.isRemoveWhenCompleted(); + break; + } + case NAME: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.name"); + description = Collections.emptyList(); + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challenge.getFriendlyName(), + (player, reply) -> { + this.challenge.setFriendlyName(reply); + this.build(); + return reply; + }); + + return true; + }; + glow = false; + break; + } + + case REQUIRED_ENTITIES: + { + List values = new ArrayList<>(this.challenge.getRequiredEntities().size()); + + for (Map.Entry entry : this.challenge.getRequiredEntities().entrySet()) + { + values.add(entry.getKey().name() + " " + entry.getValue()); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.entities"); + description = values; + icon = new ItemStack(Material.CREEPER_HEAD); + clickHandler = (panel, user, clickType, slot) -> { + new ManageEntitiesGUI(this.addon, + this.world, + this.user, + this.challenge.getRequiredEntities(), + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + break; + } + case REMOVE_ENTITIES: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-entities"); + + if (this.challenge.isRemoveEntities()) + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setRemoveEntities(!this.challenge.isRemoveEntities()); + + this.build(); + return true; + }; + glow = this.challenge.isRemoveEntities(); + break; + } + case REQUIRED_BLOCKS: + { + List values = new ArrayList<>(this.challenge.getRequiredBlocks().size()); + + for (Map.Entry entry : this.challenge.getRequiredBlocks().entrySet()) + { + values.add(entry.getKey().name() + " " + entry.getValue()); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.blocks"); + description = values; + icon = new ItemStack(Material.STONE); + clickHandler = (panel, user, clickType, slot) -> { + new ManageBlocksGUI(this.addon, + this.world, + this.user, + this.challenge.getRequiredBlocks(), + this.topLabel, + this.permissionPrefix, + this).build(); + + return true; + }; + glow = false; + break; + } + case REMOVE_BLOCKS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-blocks"); + + if (this.challenge.isRemoveBlocks()) + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setRemoveBlocks(!this.challenge.isRemoveBlocks()); + + this.build(); + return true; + }; + glow = this.challenge.isRemoveBlocks(); + break; + } + case SEARCH_RADIUS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.search-radius"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.search-radius", + "[value]", + Integer.toString(this.challenge.getSearchRadius()))); + icon = new ItemStack(Material.COBBLESTONE_WALL); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getSearchRadius(), 0, (status, value) -> { + if (status) + { + this.challenge.setSearchRadius(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REQUIRED_PERMISSIONS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.permissions"); + description = new ArrayList<>(this.challenge.getRequiredPermissions()); + icon = new ItemStack(Material.REDSTONE_LAMP); + clickHandler = (panel, user, clickType, slot) -> { + new StringListGUI(this.user, this.challenge.getRequiredPermissions(), (status, value) -> { + if (status) + { + this.challenge.setRequiredPermissions(new HashSet<>(value)); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REQUIRED_ITEMS: + { + List values = new ArrayList<>(this.challenge.getRequiredItems().size()); + + for (ItemStack itemStack : this.challenge.getRequiredItems()) + { + values.add(itemStack.getType().name() + " " + itemStack.getAmount()); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.required-items"); + description = values; + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + new ItemSwitchGUI(this.user, this.challenge.getRequiredItems(), (status, value) -> { + if (status) + { + this.challenge.setRequiredItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REMOVE_ITEMS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-items"); + + if (this.challenge.isTakeItems()) + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setTakeItems(!this.challenge.isTakeItems()); + + this.build(); + return true; + }; + glow = this.challenge.isTakeItems(); + break; + } + case REQUIRED_EXPERIENCE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.required-exp"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.required-exp", + "[value]", + Integer.toString(this.challenge.getRequiredExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getRequiredExperience(), 0, (status, value) -> { + if (status) + { + this.challenge.setRequiredExperience(value); + } + + this.build(); + }); + return true; + }; + glow = false; + break; + } + case REMOVE_EXPERIENCE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-exp"); + + if (this.challenge.isTakeExperience()) + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setTakeExperience(!this.challenge.isTakeExperience()); + + this.build(); + return true; + }; + glow = this.challenge.isTakeExperience(); + break; + } + case REQUIRED_LEVEL: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.required-level"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.required-level", + "[value]", + Long.toString(this.challenge.getRequiredIslandLevel()))); + + if (this.addon.isLevelProvided()) + { + icon = new ItemStack(Material.BEACON); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, (int) this.challenge.getRequiredIslandLevel(), (status, value) -> { + if (status) + { + this.challenge.setRequiredIslandLevel(value); + } + + this.build(); + }); + + return true; + }; + } + else + { + icon = new ItemStack(Material.BARRIER); + clickHandler = null; + } + + glow = false; + break; + } + case REQUIRED_MONEY: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.required-money"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.required-money", + "[value]", + Integer.toString(this.challenge.getRequiredMoney()))); + + if (this.addon.isEconomyProvided()) + { + icon = new ItemStack(Material.GOLD_INGOT); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getRequiredMoney(), 0, (status, value) -> { + if (status) + { + this.challenge.setRequiredMoney(value); + } + + this.build(); + }); + return true; + }; + } + else + { + icon = new ItemStack(Material.BARRIER); + clickHandler = null; + } + + glow = false; + break; + } + case REMOVE_MONEY: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-money"); + + if (this.challenge.isTakeMoney()) + { + description = Collections.singletonList(this.user + .getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user + .getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + if (this.addon.isEconomyProvided()) + { + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setTakeMoney(!this.challenge.isTakeMoney()); + + this.build(); + return true; + }; + } + else + { + icon = new ItemStack(Material.BARRIER); + clickHandler = null; + } + + glow = this.challenge.isTakeMoney(); + break; + } + + case REWARD_TEXT: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-text"); + description = Collections.singletonList(this.challenge.getRewardText()); + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challenge.getRewardText(), + (player, reply) -> { + this.challenge.setRewardText(reply); + this.build(); + return reply; + }); + + return true; + }; + glow = false; + break; + } + case REWARD_ITEM: + { + List values = new ArrayList<>(this.challenge.getRewardItems().size()); + + for (ItemStack itemStack : this.challenge.getRewardItems()) + { + values.add(itemStack.getType().name() + " " + itemStack.getAmount()); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-items"); + description = values; + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + new ItemSwitchGUI(this.user, this.challenge.getRewardItems(), (status, value) -> { + if (status) + { + this.challenge.setRewardItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REWARD_EXPERIENCE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-exp"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.reward-exp", + "[value]", + Integer.toString(this.challenge.getRewardExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getRewardExperience(), 0, (status, value) -> { + if (status) + { + this.challenge.setRewardExperience(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REWARD_MONEY: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-money"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.reward-money", + "[value]", + Integer.toString(this.challenge.getRewardMoney()))); + + if (this.addon.isEconomyProvided()) + { + icon = new ItemStack(Material.GOLD_INGOT); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getRewardMoney(), 0, (status, value) -> { + if (status) + { + this.challenge.setRewardMoney(value); + } + + this.build(); + }); + + return true; + }; + } + else + { + icon = new ItemStack(Material.BARRIER); + clickHandler = null; + } + + glow = false; + break; + } + case REWARD_COMMANDS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-command"); + description = this.challenge.getRewardCommands(); + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> { + new StringListGUI(this.user, this.challenge.getRewardCommands(), (status, value) -> { + if (status) + { + this.challenge.setRewardCommands(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + + case REPEATABLE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.repeatable"); + + if (this.challenge.isRepeatable()) + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.enabled")); + } + else + { + description = Collections.singletonList(this.user.getTranslation("challenges.gui.admin.descriptions.disabled")); + } + + icon = new ItemStack(Material.LEVER); + clickHandler = (panel, user, clickType, slot) -> { + this.challenge.setRepeatable(!this.challenge.isRepeatable()); + + this.build(); + return true; + }; + glow = this.challenge.isRepeatable(); + break; + } + case REPEAT_COUNT: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.repeat-count"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.repeat-count", + "[value]", + Integer.toString(this.challenge.getMaxTimes()))); + icon = new ItemStack(Material.COBBLESTONE_WALL); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getMaxTimes(), 0, (status, value) -> { + if (status) + { + this.challenge.setMaxTimes(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + + case REPEAT_REWARD_TEXT: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.repeat-reward-text"); + description = Collections.singletonList(this.challenge.getRepeatRewardText()); + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challenge.getRepeatRewardText(), + (player, reply) -> { + this.challenge.setRepeatRewardText(reply); + this.build(); + return reply; + }); + + return true; + }; + glow = false; + break; + } + case REPEAT_REWARD_ITEM: + { + List values = new ArrayList<>(this.challenge.getRepeatItemReward().size()); + + for (ItemStack itemStack : this.challenge.getRepeatItemReward()) + { + values.add(itemStack.getType().name() + " " + itemStack.getAmount()); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.repeat-reward-items"); + description = values; + icon = new ItemStack(Material.TRAPPED_CHEST); + clickHandler = (panel, user, clickType, slot) -> { + new ItemSwitchGUI(this.user, this.challenge.getRepeatItemReward(), (status, value) -> { + if (status) + { + this.challenge.setRepeatItemReward(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REPEAT_REWARD_EXPERIENCE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.repeat-reward-exp"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.repeat-reward-exp", + "[value]", + Integer.toString(this.challenge.getRepeatExperienceReward()))); + icon = new ItemStack(Material.GLASS_BOTTLE); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challenge.getRepeatExperienceReward(), 0, (status, value) -> { + if (status) + { + this.challenge.setRepeatExperienceReward(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REPEAT_REWARD_MONEY: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.repeat-reward-money"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.repeat-reward-money", + "[value]", + Integer.toString(this.challenge.getRepeatMoneyReward()))); + + if (this.addon.isEconomyProvided()) + { + icon = new ItemStack(Material.GOLD_NUGGET); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, + this.challenge.getRepeatMoneyReward(), + 0, + (status, value) -> { + if (status) + { + this.challenge.setRepeatMoneyReward(value); + } + + this.build(); + }); + + return true; + }; + } + else + { + icon = new ItemStack(Material.BARRIER); + clickHandler = null; + } + + glow = false; + break; + } + case REPEAT_REWARD_COMMANDS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.repeat-reward-command"); + description = this.challenge.getRepeatRewardCommands(); + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> { + new StringListGUI(this.user, this.challenge.getRepeatRewardCommands(), (status, value) -> { + if (status) + { + this.challenge.setRepeatRewardCommands(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), glow, clickHandler, false); + } + + + /** + * This method returns next challenge type from given. + * @param type Given challenge type. + * @return Next Challenge Type. + */ + private Challenge.ChallengeType getNextType(Challenge.ChallengeType type) + { + Challenge.ChallengeType[] values = Challenge.ChallengeType.values(); + + for (int i = 0; i < values.length; i++) + { + if (values[i].equals(type)) + { + if (i + 1 == values.length) + { + return values[0]; + } + else + { + return values[i + 1]; + } + } + } + + return type; + } + + + /** + * This method returns previous challenge type from given. + * @param type Given challenge type. + * @return Previous Challenge Type. + */ + private Challenge.ChallengeType getPreviousType(Challenge.ChallengeType type) + { + Challenge.ChallengeType[] values = Challenge.ChallengeType.values(); + + for (int i = 0; i < values.length; i++) + { + if (values[i].equals(type)) + { + if (i > 0) + { + return values[i - 1]; + } + else + { + return values[values.length - 1]; + } + } + } + + return type; + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Represents different types of menus + */ + private enum MenuType + { + PROPERTIES, + REQUIREMENTS, + REWARDS + } + + + /** + * Represents different buttons that could be in menus. + */ + private enum Button + { + NAME, + TYPE, + DEPLOYED, + ICON, + DESCRIPTION, + ORDER, + ENVIRONMENT, + REMOVE_ON_COMPLETE, + + REQUIRED_ENTITIES, + REMOVE_ENTITIES, + REQUIRED_BLOCKS, + REMOVE_BLOCKS, + SEARCH_RADIUS, + REQUIRED_PERMISSIONS, + REQUIRED_ITEMS, + REMOVE_ITEMS, + REQUIRED_EXPERIENCE, + REMOVE_EXPERIENCE, + REQUIRED_LEVEL, + REQUIRED_MONEY, + REMOVE_MONEY, + + REWARD_TEXT, + REWARD_ITEM, + REWARD_EXPERIENCE, + REWARD_MONEY, + REWARD_COMMANDS, + + REPEATABLE, + REPEAT_COUNT, + + REPEAT_REWARD_TEXT, + REPEAT_REWARD_ITEM, + REPEAT_REWARD_EXPERIENCE, + REPEAT_REWARD_MONEY, + REPEAT_REWARD_COMMANDS, + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * Variable holds challenge thats needs editing. + */ + private Challenge challenge; + + /** + * Variable holds current active menu. + */ + private MenuType currentMenuType; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java new file mode 100644 index 0000000..75c7159 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditLevelGUI.java @@ -0,0 +1,648 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.wesjd.anvilgui.AnvilGUI; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.ItemSwitchGUI; +import world.bentobox.challenges.panel.util.NumberGUI; +import world.bentobox.challenges.panel.util.SelectChallengeGUI; +import world.bentobox.challenges.panel.util.StringListGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class contains all necessary elements to create Levels Edit GUI. +*/ +public class EditLevelGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + * @param challengeLevel ChallengeLevel that must be edited. + */ + public EditLevelGUI(ChallengesAddon addon, + World world, + User user, + ChallengeLevel challengeLevel, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, challengeLevel, topLabel, permissionPrefix, null); + } + + + /** + * {@inheritDoc} + * @param challengeLevel ChallengeLevel that must be edited. + */ + public EditLevelGUI(ChallengesAddon addon, + World world, + User user, + ChallengeLevel challengeLevel, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + this.challengeLevel = challengeLevel; + this.currentMenuType = MenuType.PROPERTIES; + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.gui.admin.edit-level-title")); + + GuiUtils.fillBorder(panelBuilder); + + panelBuilder.item(2, this.createMenuButton(MenuType.PROPERTIES)); + panelBuilder.item(4, this.createMenuButton(MenuType.REWARDS)); + panelBuilder.item(6, this.createMenuButton(MenuType.CHALLENGES)); + + if (this.currentMenuType.equals(MenuType.PROPERTIES)) + { + this.buildMainPropertiesPanel(panelBuilder); + } + else if (this.currentMenuType.equals(MenuType.CHALLENGES)) + { + this.buildChallengesPanel(panelBuilder); + } + else if (this.currentMenuType.equals(MenuType.REWARDS)) + { + this.buildRewardsPanel(panelBuilder); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This class populate LevelsEditGUI with main level settings. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildMainPropertiesPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(10, this.createButton(Button.NAME)); + + panelBuilder.item(19, this.createButton(Button.ICON)); + panelBuilder.item(22, this.createButton(Button.UNLOCK_MESSAGE)); + panelBuilder.item(25, this.createButton(Button.ORDER)); + + panelBuilder.item(31, this.createButton(Button.WAIVER_AMOUNT)); + } + + + /** + * This class populate LevelsEditGUI with level rewards. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildRewardsPanel(PanelBuilder panelBuilder) + { + panelBuilder.item(12, this.createButton(Button.REWARD_DESCRIPTION)); + panelBuilder.item(21, this.createButton(Button.REWARD_COMMANDS)); + + panelBuilder.item(13, this.createButton(Button.REWARD_ITEM)); + panelBuilder.item(22, this.createButton(Button.REWARD_EXPERIENCE)); + panelBuilder.item(31, this.createButton(Button.REWARD_MONEY)); + } + + + /** + * This class populate LevelsEditGUI with level challenges. + * @param panelBuilder PanelBuilder where icons must be added. + */ + private void buildChallengesPanel(PanelBuilder panelBuilder) + { + List challengeList = this.addon.getChallengesManager().getLevelChallenges(this.challengeLevel); + + final int MAX_ELEMENTS = 21; + + if (this.pageIndex < 0) + { + this.pageIndex = challengeList.size() / MAX_ELEMENTS; + } + else if (this.pageIndex > (challengeList.size() / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int challengeIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (challengeIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + challengeIndex < challengeList.size() && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createChallengeIcon(challengeList.get(challengeIndex++))); + } + + index++; + } + + // Navigation buttons only if necessary + if (challengeList.size() > MAX_ELEMENTS) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + } + + panelBuilder.item(39, this.createButton(Button.ADD_CHALLENGE)); + panelBuilder.item(41, this.createButton(Button.REMOVE_CHALLENGE)); + } + + +// --------------------------------------------------------------------- +// Section: Other methods +// --------------------------------------------------------------------- + + + /** + * This method creates top menu buttons, that allows to switch "tabs". + * @param menuType Menu Type which button must be constructed. + * @return PanelItem that represents given menu type. + */ + private PanelItem createMenuButton(MenuType menuType) + { + ItemStack icon; + String name; + List description; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (menuType) + { + case PROPERTIES: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.properties"); + description = Collections.emptyList(); + icon = new ItemStack(Material.CRAFTING_TABLE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.PROPERTIES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.PROPERTIES); + break; + } + case CHALLENGES: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.challenges"); + description = Collections.emptyList(); + icon = new ItemStack(Material.RAIL); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.CHALLENGES; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.CHALLENGES); + break; + } + case REWARDS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.rewards"); + description = Collections.emptyList(); + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + this.currentMenuType = MenuType.REWARDS; + this.build(); + + return true; + }; + glow = this.currentMenuType.equals(MenuType.REWARDS); + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), glow, clickHandler, false); + } + + + /** + * This method creates given challenge icon. On click it should open Edit Challenge GUI. + * @param challenge Challenge which icon must be created. + * @return PanelItem that represents given challenge. + */ + private PanelItem createChallengeIcon(Challenge challenge) + { + return new PanelItemBuilder(). + name(challenge.getFriendlyName()). + description(GuiUtils.stringSplit(challenge.getDescription())). + icon(challenge.getIcon()). + clickHandler((panel, user1, clickType, slot) -> { + // Open challenges edit screen. + new EditChallengeGUI(this.addon, + this.world, + this.user, + challenge, + this.topLabel, + this.permissionPrefix, + this).build(); + return true; + }). + glow(!challenge.isDeployed()). + build(); + + } + + + /** + * This method creates buttons for default main menu. + * @param button Button which panel item must be created. + * @return PanelItem that represents given button. + */ + private PanelItem createButton(Button button) + { + ItemStack icon; + String name; + List description; + boolean glow; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case NAME: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.name"); + description = Collections.singletonList(this.challengeLevel.getFriendlyName()); + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challengeLevel.getFriendlyName(), + (player, reply) -> { + this.challengeLevel.setFriendlyName(reply); + this.build(); + return reply; + }); + + return true; + }; + glow = false; + break; + } + case ICON: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.icon"); + description = Collections.emptyList(); + icon = this.challengeLevel.getIcon(); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challengeLevel.getIcon().getType().name(), + (player, reply) -> { + Material material = Material.getMaterial(reply); + + if (material != null) + { + this.challengeLevel.setIcon(new ItemStack(material)); + this.build(); + } + else + { + this.user.sendMessage("challenges.errors.wrong-icon", "[value]", reply); + } + + return reply; + }); + + return true; + }; + glow = false; + break; + } + case UNLOCK_MESSAGE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.description"); + description = Collections.singletonList(this.challengeLevel.getUnlockMessage()); + icon = new ItemStack(Material.WRITABLE_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challengeLevel.getUnlockMessage(), + (player, reply) -> { + this.challengeLevel.setUnlockMessage(reply); + this.build(); + return reply; + }); + return true; + }; + glow = false; + break; + } + case ORDER: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.order"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.order", + "[value]", + Integer.toString(this.challengeLevel.getOrder()))); + icon = new ItemStack(Material.DROPPER); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challengeLevel.getOrder(), -1, 54, (status, value) -> { + if (status) + { + this.challengeLevel.setOrder(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case WAIVER_AMOUNT: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.waiver-amount"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.waiver-amount", + "[value]", + Integer.toString(this.challengeLevel.getWaiverAmount()))); + icon = new ItemStack(Material.REDSTONE_TORCH); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challengeLevel.getWaiverAmount(), 0, (status, value) -> { + if (status) + { + this.challengeLevel.setWaiverAmount(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + + case REWARD_DESCRIPTION: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-text"); + description = Collections.singletonList(this.challengeLevel.getRewardText()); + icon = new ItemStack(Material.WRITTEN_BOOK); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(this.addon.getPlugin(), + this.user.getPlayer(), + this.challengeLevel.getRewardText(), + (player, reply) -> { + this.challengeLevel.setRewardText(reply); + this.build(); + return reply; + }); + return true; + }; + glow = false; + break; + } + case REWARD_ITEM: + { + List values = new ArrayList<>(this.challengeLevel.getRewardItems().size()); + + for (ItemStack itemStack : this.challengeLevel.getRewardItems()) + { + values.add(itemStack.getType().name() + " " + itemStack.getAmount()); + } + + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-items"); + description = values; + icon = new ItemStack(Material.CHEST); + clickHandler = (panel, user, clickType, slot) -> { + new ItemSwitchGUI(this.user, this.challengeLevel.getRewardItems(), (status, value) -> { + if (status) + { + this.challengeLevel.setRewardItems(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REWARD_EXPERIENCE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-exp"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.reward-exp", + "[value]", + Integer.toString(this.challengeLevel.getRewardExperience()))); + icon = new ItemStack(Material.EXPERIENCE_BOTTLE); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challengeLevel.getRewardExperience(), 0, (status, value) -> { + if (status) + { + this.challengeLevel.setRewardExperience(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REWARD_MONEY: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-money"); + description = Collections.singletonList( + this.user.getTranslation("challenges.gui.admin.descriptions.reward-money", + "[value]", + Integer.toString(this.challengeLevel.getRewardMoney()))); + + if (this.addon.isEconomyProvided()) + { + icon = new ItemStack(Material.GOLD_INGOT); + clickHandler = (panel, user, clickType, slot) -> { + new NumberGUI(this.user, this.challengeLevel.getRewardMoney(), 0, (status, value) -> { + if (status) + { + this.challengeLevel.setRewardMoney(value); + } + + this.build(); + }); + + return true; + }; + } + else + { + icon = new ItemStack(Material.BARRIER); + clickHandler = null; + } + + glow = false; + break; + } + case REWARD_COMMANDS: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.reward-command"); + description = this.challengeLevel.getRewardCommands(); + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> { + new StringListGUI(this.user, this.challengeLevel.getRewardCommands(), (status, value) -> { + if (status) + { + this.challengeLevel.setRewardCommands(value); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + + case ADD_CHALLENGE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.add-challenge"); + description = Collections.emptyList(); + icon = new ItemStack(Material.WATER_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + // Get all challenge that is not in current challenge. + List challengeList = manager.getAllChallenges(this.world); + challengeList.removeAll(manager.getLevelChallenges(this.challengeLevel)); + + new SelectChallengeGUI(this.user, challengeList, (status, value) -> { + if (status) + { + manager.addChallengeToLevel(value, this.challengeLevel); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + case REMOVE_CHALLENGE: + { + name = this.user.getTranslation("challenges.gui.admin.buttons.remove-challenge"); + description = Collections.emptyList(); + icon = new ItemStack(Material.LAVA_BUCKET); + clickHandler = (panel, user, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + new SelectChallengeGUI(this.user, manager.getLevelChallenges(this.challengeLevel), (status, value) -> { + if (status) + { + manager.removeChallengeFromLevel(value, this.challengeLevel); + } + + this.build(); + }); + + return true; + }; + glow = false; + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), glow, clickHandler, false); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Represents different buttons that could be in menus. + */ + private enum Button + { + NAME, + ICON, + UNLOCK_MESSAGE, + ORDER, + WAIVER_AMOUNT, + + REWARD_DESCRIPTION, + REWARD_ITEM, + REWARD_EXPERIENCE, + REWARD_MONEY, + REWARD_COMMANDS, + + ADD_CHALLENGE, + REMOVE_CHALLENGE + } + + + /** + * Represents different types of menus + */ + private enum MenuType + { + PROPERTIES, + CHALLENGES, + REWARDS + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This variable holds current challenge level that is in editing GUI. + */ + private ChallengeLevel challengeLevel; + + /** + * Variable holds current active menu. + */ + private MenuType currentMenuType; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java new file mode 100644 index 0000000..984e9ce --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/EditSettingsGUI.java @@ -0,0 +1,141 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; + +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This Class creates GUI that allows to change Challenges Addon Settings via in-game + * menu. + */ +public class EditSettingsGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + public EditSettingsGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, topLabel, permissionPrefix, null); + } + + + /** + * {@inheritDoc} + */ + public EditSettingsGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.gui.admin.settings-title")); + + GuiUtils.fillBorder(panelBuilder); + + // resetChallenges + panelBuilder.item(19, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.reset")). + description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.admin.descriptions.reset"))). + icon(Material.LAVA_BUCKET). + clickHandler((panel, user1, clickType, i) -> { + this.addon.getChallengesSettings().setResetChallenges( + !this.addon.getChallengesSettings().isResetChallenges()); + this.build(); + return true; + }). + glow(this.addon.getChallengesSettings().isResetChallenges()). + build()); + + // broadcastMessages + panelBuilder.item(20, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.broadcast")). + description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.admin.descriptions.broadcast"))). + icon(Material.JUKEBOX). + clickHandler((panel, user1, clickType, i) -> { + this.addon.getChallengesSettings().setBroadcastMessages( + !this.addon.getChallengesSettings().isBroadcastMessages()); + this.build(); + return true; + }). + glow(this.addon.getChallengesSettings().isBroadcastMessages()). + build()); + + // removeCompleteOneTimeChallenges + panelBuilder.item(21, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.remove-on-complete")). + description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.admin.descriptions.remove-on-complete"))). + icon(Material.MAGMA_BLOCK). + clickHandler((panel, user1, clickType, i) -> { + this.addon.getChallengesSettings().setRemoveCompleteOneTimeChallenges( + !this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()); + this.build(); + return true; + }). + glow(this.addon.getChallengesSettings().isRemoveCompleteOneTimeChallenges()). + build()); + + // addCompletedGlow + panelBuilder.item(22, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.glow")). + description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.admin.descriptions.glow"))). + icon(Material.GLOWSTONE). + clickHandler((panel, user1, clickType, i) -> { + this.addon.getChallengesSettings().setAddCompletedGlow( + !this.addon.getChallengesSettings().isAddCompletedGlow()); + this.build(); + return true; + }). + glow(this.addon.getChallengesSettings().isAddCompletedGlow()). + build()); + + // freeChallengesAtTheTop + panelBuilder.item(23, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.free-challenges")). + description(GuiUtils.stringSplit(this.user.getTranslation("challenges.gui.admin.descriptions.free-challenges"))). + icon(Material.FILLED_MAP). + clickHandler((panel, user1, clickType, i) -> { + this.addon.getChallengesSettings().setFreeChallengesFirst( + !this.addon.getChallengesSettings().isFreeChallengesFirst()); + this.build(); + return true; + }). + glow(this.addon.getChallengesSettings().isFreeChallengesFirst()). + build()); + + // Return Button + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java new file mode 100644 index 0000000..ad76912 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListChallengesGUI.java @@ -0,0 +1,194 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.ConfirmationGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class contains all necessary elements to create GUI that lists all challenges. + * It allows to edit them or remove, depending on given input mode. + */ +public class ListChallengesGUI extends CommonGUI +{ + // --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + * @param mode - mode that indicate what should do icon clicking. + */ + public ListChallengesGUI(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, mode, topLabel, permissionPrefix, null); + } + + + /** + * {@inheritDoc} + * @param mode - mode that indicate what should do icon clicking. + */ + public ListChallengesGUI(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + this.currentMode = mode; + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.gui.admin.choose-challenge-title")); + + if (this.currentMode.equals(Mode.DELETE)) + { + GuiUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); + } + else + { + GuiUtils.fillBorder(panelBuilder); + } + + List challengeList = this.addon.getChallengesManager().getAllChallenges(this.world); + + final int MAX_ELEMENTS = 21; + + if (this.pageIndex < 0) + { + this.pageIndex = challengeList.size() / MAX_ELEMENTS; + } + else if (this.pageIndex > (challengeList.size() / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int challengeIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (challengeIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + challengeIndex < challengeList.size() && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createChallengeIcon(challengeList.get(challengeIndex++))); + } + + index++; + } + + // Navigation buttons only if necessary + if (challengeList.size() > MAX_ELEMENTS) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates button for given challenge. + * @param challenge Challenge which button must be created. + * @return Challenge button. + */ + private PanelItem createChallengeIcon(Challenge challenge) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(challenge.getFriendlyName()). + description(GuiUtils.stringSplit(challenge.getDescription())). + icon(challenge.getIcon()). + glow(challenge.isDeployed()); + + if (this.currentMode.equals(Mode.EDIT)) + { + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + new EditChallengeGUI(this.addon, + this.world, + this.user, + challenge, + this.topLabel, + this.permissionPrefix, + this).build(); + return true; + }); + } + else if (this.currentMode.equals(Mode.DELETE)) + { + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + new ConfirmationGUI(this.user, value -> { + if (value) + { + this.addon.getChallengesManager().deleteChallenge(challenge); + } + }); + return true; + }); + } + + return itemBuilder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Mode in which gui icons should processed. + */ + public enum Mode + { + EDIT, + DELETE + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Current mode in which icons will act. + */ + private Mode currentMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java new file mode 100644 index 0000000..5267d10 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListLevelsGUI.java @@ -0,0 +1,195 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Material; +import org.bukkit.World; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.database.object.ChallengeLevel; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.ConfirmationGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class creates GUI that lists all Levels. Clicking on Level icon will be processed + * by input mode. + */ +public class ListLevelsGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + * @param mode - mode that indicate what should do icon clicking. + */ + public ListLevelsGUI(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, mode, topLabel, permissionPrefix, null); + } + + + /** + * {@inheritDoc} + * @param mode - mode that indicate what should do icon clicking. + */ + public ListLevelsGUI(ChallengesAddon addon, + World world, + User user, + Mode mode, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + this.currentMode = mode; + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.gui.admin.choose-level-title")); + + if (this.currentMode.equals(Mode.DELETE)) + { + GuiUtils.fillBorder(panelBuilder, Material.RED_STAINED_GLASS_PANE); + } + else + { + GuiUtils.fillBorder(panelBuilder); + } + + List levelList = this.addon.getChallengesManager().getLevels(this.world); + + final int MAX_ELEMENTS = 21; + + if (this.pageIndex < 0) + { + this.pageIndex = levelList.size() / MAX_ELEMENTS; + } + else if (this.pageIndex > (levelList.size() / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int levelIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (levelIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + levelIndex < levelList.size() && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createLevelIcon(levelList.get(levelIndex++))); + } + + index++; + } + + // Navigation buttons only if necessary + if (levelList.size() > MAX_ELEMENTS) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + } + + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates button for given level + * @param challengeLevel Level which button must be created. + * @return Level button. + */ + private PanelItem createLevelIcon(ChallengeLevel challengeLevel) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(). + name(challengeLevel.getFriendlyName()). + description(GuiUtils.stringSplit(challengeLevel.getUnlockMessage())). + icon(challengeLevel.getIcon()). + glow(false); + + if (this.currentMode.equals(Mode.EDIT)) + { + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + new EditLevelGUI(this.addon, + this.world, + this.user, + challengeLevel, + this.topLabel, + this.permissionPrefix, + this).build(); + return true; + }); + } + else if (this.currentMode.equals(Mode.DELETE)) + { + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + new ConfirmationGUI(this.user, value -> { + if (value) + { + this.addon.getChallengesManager(). + deleteChallengeLevel(challengeLevel); + } + }); + return true; + }); + } + + return itemBuilder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Mode in which gui icons should processed. + */ + public enum Mode + { + EDIT, + DELETE + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Current mode in which icons will act. + */ + private Mode currentMode; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java new file mode 100644 index 0000000..c7c5461 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ListUsersGUI.java @@ -0,0 +1,299 @@ +package world.bentobox.challenges.panel.admin; + + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.Player; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Players; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.ConfirmationGUI; +import world.bentobox.challenges.panel.util.SelectChallengeGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class contains methods that allows to select specific user. + */ +public class ListUsersGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with players that should be in GUI. + */ + private List onlineUsers; + + /** + * Current operation mode. + */ + private Mode operationMode; + + /** + * Current index of view mode + */ + private int modeIndex = 2; + + /** + * This allows to switch which users should be in the list. + */ + private enum ViewMode + { + ONLINE, + WITH_ISLAND, + IN_WORLD + } + + /** + * This allows to decide what User Icon should do. + */ + public enum Mode + { + COMPLETE, + RESET, + RESET_ALL + } + + +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + + /** + * {@inheritDoc} + * @param operationMode Indicate what should happen on player icon click. + */ + public ListUsersGUI(ChallengesAddon addon, + World world, + User user, + Mode operationMode, + String topLabel, + String permissionPrefix) + { + this(addon, world, user, operationMode, topLabel, permissionPrefix, null); + } + + + /** + * {@inheritDoc} + * @param operationMode Indicate what should happen on player icon click. + */ + public ListUsersGUI(ChallengesAddon addon, + World world, + User user, + Mode operationMode, + String topLabel, + String permissionPrefix, + CommonGUI parentPanel) + { + super(addon, world, user, topLabel, permissionPrefix, parentPanel); + this.onlineUsers = this.collectUsers(ViewMode.IN_WORLD); + this.operationMode = operationMode; + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name( + this.user.getTranslation("challenges.gui.admin.choose-user-title")); + + GuiUtils.fillBorder(panelBuilder); + + final int MAX_ELEMENTS = 21; + + if (this.pageIndex < 0) + { + this.pageIndex = this.onlineUsers.size() / MAX_ELEMENTS; + } + else if (this.pageIndex > (this.onlineUsers.size() / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int playerIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (playerIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + playerIndex < this.onlineUsers.size() && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createPlayerIcon(this.onlineUsers.get(playerIndex++))); + } + + index++; + } + + // Add button that allows to toogle different player lists. + panelBuilder.item( 4, this.createToggleButton()); + + // Navigation buttons only if necessary + if (this.onlineUsers.size() > MAX_ELEMENTS) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + } + + panelBuilder.item(44, this.returnButton); + + + panelBuilder.build(); + } + + + /** + * This method creates button for given user. If user has island it will add valid click handler. + * @param player Player which button must be created. + * @return Player button. + */ + private PanelItem createPlayerIcon(Player player) + { + if (this.addon.getIslands().hasIsland(this.world, player.getUniqueId())) + { + return new PanelItemBuilder().name(player.getName()).icon(player.getName()).clickHandler( + (panel, user1, clickType, slot) -> { + ChallengesManager manager = this.addon.getChallengesManager(); + + switch (this.operationMode) + { + case COMPLETE: + new SelectChallengeGUI(this.user, manager.getAllChallenges(this.world), (status, value) -> { + if (status) + { + manager.setChallengeComplete(User.getInstance(player), value); + } + + this.build(); + }); + break; + case RESET: + new SelectChallengeGUI(this.user, manager.getAllChallenges(this.world), (status, value) -> { + if (status) + { + manager.resetChallenge(User.getInstance(player), value); + } + + this.build(); + }); + break; + case RESET_ALL: + new ConfirmationGUI(this.user, status -> { + if (status) + { + manager.resetAllChallenges(this.user, this.world); + } + }); + break; + } + + return true; + }).build(); + } + else + { + return new PanelItemBuilder(). + name(player.getName()). + icon(Material.BARRIER). + description(GuiUtils.stringSplit(this.user.getTranslation("general.errors.player-has-no-island"))). + clickHandler((panel, user1, clickType, slot) -> false). + build(); + } + } + + + /** + * This method collects users based on view mode. + * @param mode Given view mode. + * @return List with players in necessary view mode. + */ + private List collectUsers(ViewMode mode) + { + if (mode.equals(ViewMode.ONLINE)) + { + return new ArrayList<>(Bukkit.getOnlinePlayers()); + } + else if (mode.equals(ViewMode.WITH_ISLAND)) + { + return this.addon.getPlayers().getPlayers().stream(). + filter(player -> this.addon.getIslands().getIsland(this.world, player.getPlayerUUID()) != null). + map(Players::getPlayer). + collect(Collectors.toList()); + } + else + { + return new ArrayList<>(this.world.getPlayers()); + } + } + + + /** + * This method creates Player List view Mode toggle button. + * @return Button that toggles through player view mode. + */ + private PanelItem createToggleButton() + { + List values = new ArrayList<>(ViewMode.values().length); + + for (int i = 0; i < ViewMode.values().length; i++) + { + values.add((this.modeIndex == i ? "&2" : "&c") + + this.user.getTranslation("challenges.gui.admin.descriptions." + + ViewMode.values()[i].name().toLowerCase())); + } + + return new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.toggle-users", + "[value]", + this.user.getTranslation("challenges.gui.admin.descriptions." + ViewMode.values()[this.modeIndex].name().toLowerCase()))). + description(GuiUtils.stringSplit(values)). + icon(Material.STONE_BUTTON). + clickHandler( + (panel, user1, clickType, slot) -> { + if (clickType.isRightClick()) + { + this.modeIndex--; + + if (this.modeIndex < 0) + { + this.modeIndex = ViewMode.values().length - 1; + } + } + else + { + this.modeIndex++; + + if (this.modeIndex >= ViewMode.values().length) + { + this.modeIndex = 0; + } + } + + this.onlineUsers = this.collectUsers(ViewMode.values()[this.modeIndex]); + this.pageIndex = 0; + this.build(); + return true; + }).build(); + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java new file mode 100644 index 0000000..28ec03d --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageBlocksGUI.java @@ -0,0 +1,225 @@ +package world.bentobox.challenges.panel.admin; + + +import org.apache.commons.lang.WordUtils; +import org.bukkit.Material; +import org.bukkit.World; +import java.util.*; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.NumberGUI; +import world.bentobox.challenges.panel.util.SelectBlocksGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class allows to edit material that are in required material map. + */ +public class ManageBlocksGUI extends CommonGUI +{ + public ManageBlocksGUI(ChallengesAddon addon, + World world, + User user, + Map materialMap, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + this.materialMap = materialMap; + + this.materialList = new ArrayList<>(this.materialMap.keySet()); + + // Sort materials by their ordinal value. + this.materialList.sort(Comparator.comparing(Enum::ordinal)); + + this.selectedMaterials = new HashSet<>(); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation("challenges.gui.admin.manage-blocks")); + + // Create nice border. + GuiUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD)); + panelBuilder.item(5, this.createButton(Button.REMOVE)); + + final int MAX_ELEMENTS = 21; + + if (this.pageIndex < 0) + { + this.pageIndex = this.materialList.size() / MAX_ELEMENTS; + } + else if (this.pageIndex > (this.materialList.size() / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int entitiesIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (entitiesIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + entitiesIndex < this.materialList.size() && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createElementButton(this.materialList.get(entitiesIndex++))); + } + + index++; + } + + // Navigation buttons only if necessary + if (this.materialList.size() > MAX_ELEMENTS) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + } + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + switch (button) + { + case ADD: + builder.name(this.user.getTranslation("challenges.gui.button.add")); + builder.icon(Material.BUCKET); + builder.clickHandler((panel, user1, clickType, slot) -> { + + new SelectBlocksGUI(this.user, new HashSet<>(this.materialList), (status, material) -> { + if (status) + { + this.materialMap.put(material, 1); + this.materialList.add(material); + } + + this.build(); + }); + return true; + }); + break; + case REMOVE: + builder.name(this.user.getTranslation("challenges.gui.button.remove-selected")); + builder.icon(Material.LAVA_BUCKET); + builder.clickHandler((panel, user1, clickType, slot) -> { + this.materialMap.keySet().removeAll(this.selectedMaterials); + this.materialList.removeAll(this.selectedMaterials); + this.build(); + return true; + }); + break; + } + + return builder.build(); + } + + + /** + * This method creates button for given material. + * @param material material which button must be created. + * @return new Button for material. + */ + private PanelItem createElementButton(Material material) + { + return new PanelItemBuilder(). + name(WordUtils.capitalize(material.name().toLowerCase().replace("_", " "))). + icon(GuiUtils.getMaterialItem(material, this.materialMap.get(material))). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (clickType.isRightClick()) + { + if (!this.selectedMaterials.add(material)) + { + // Remove material if it is already selected + this.selectedMaterials.remove(material); + } + + this.build(); + } + else + { + new NumberGUI(this.user, this.materialMap.get(material), 1, (status, value) -> { + if (status) + { + // Update value only when something changes. + this.materialMap.put(material, value); + } + + this.build(); + }); + } + return true; + }). + glow(this.selectedMaterials.contains(material)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ADD, + REMOVE + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * Contains selected materials. + */ + private Set selectedMaterials; + + /** + * List of materials to avoid order issues. + */ + private List materialList; + + /** + * List of required materials. + */ + private Map materialMap; +} diff --git a/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java new file mode 100644 index 0000000..d2b4d71 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/admin/ManageEntitiesGUI.java @@ -0,0 +1,234 @@ +package world.bentobox.challenges.panel.admin; + + +import org.apache.commons.lang.WordUtils; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.*; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.util.NumberGUI; +import world.bentobox.challenges.panel.util.SelectEntityGUI; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class allows to edit entities that are in required entities map. + */ +public class ManageEntitiesGUI extends CommonGUI +{ + public ManageEntitiesGUI(ChallengesAddon addon, + World world, + User user, + Map requiredEntities, + String topLabel, + String permissionPrefix, + CommonGUI parentGUI) + { + super(addon, world, user, topLabel, permissionPrefix, parentGUI); + this.requiredEntities = requiredEntities; + + this.entityList = new ArrayList<>(this.requiredEntities.keySet()); + this.entityList.sort(Comparator.comparing(Enum::name)); + + this.selectedEntities = new HashSet<>(EntityType.values().length); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation("challenges.gui.admin.edit-entities")); + + // create border + GuiUtils.fillBorder(panelBuilder); + + panelBuilder.item(3, this.createButton(Button.ADD)); + panelBuilder.item(5, this.createButton(Button.REMOVE)); + + final int MAX_ELEMENTS = 21; + + if (this.pageIndex < 0) + { + this.pageIndex = this.entityList.size() / MAX_ELEMENTS; + } + else if (this.pageIndex > (this.entityList.size() / MAX_ELEMENTS)) + { + this.pageIndex = 0; + } + + int entitiesIndex = MAX_ELEMENTS * this.pageIndex; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (entitiesIndex < ((this.pageIndex + 1) * MAX_ELEMENTS) && + entitiesIndex < this.entityList.size() && + index < 26) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createEntityButton(this.entityList.get(entitiesIndex++))); + } + + index++; + } + + // Navigation buttons only if necessary + if (this.entityList.size() > MAX_ELEMENTS) + { + panelBuilder.item(18, this.getButton(CommonButtons.PREVIOUS)); + panelBuilder.item(26, this.getButton(CommonButtons.NEXT)); + } + + // Add return button. + panelBuilder.item(44, this.returnButton); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem button of requested type. + * @param button Button which must be created. + * @return new PanelItem with requested functionality. + */ + private PanelItem createButton(Button button) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + switch (button) + { + case ADD: + builder.name(this.user.getTranslation("challenges.gui.button.add")); + builder.icon(Material.BUCKET); + builder.clickHandler((panel, user1, clickType, slot) -> { + new SelectEntityGUI(this.user, (status, entity) -> { + if (status) + { + if (!this.requiredEntities.containsKey(entity)) + { + this.requiredEntities.put(entity, 1); + this.entityList.add(entity); + } + } + + this.build(); + }); + return true; + }); + break; + case REMOVE: + builder.name(this.user.getTranslation("challenges.gui.button.remove-selected")); + builder.icon(Material.LAVA_BUCKET); + builder.clickHandler((panel, user1, clickType, slot) -> { + this.requiredEntities.keySet().removeAll(this.selectedEntities); + this.entityList.removeAll(this.selectedEntities); + this.build(); + return true; + }); + break; + } + + return builder.build(); + } + + + /** + * This method creates button for given entity. + * @param entity Entity which button must be created. + * @return new Button for entity. + */ + private PanelItem createEntityButton(EntityType entity) + { + return new PanelItemBuilder(). + name(WordUtils.capitalize(entity.name().toLowerCase().replace("_", " "))). + icon(this.asEggs ? + GuiUtils.getEntityEgg(entity, this.requiredEntities.get(entity)) : + GuiUtils.getEntityHead(entity, this.requiredEntities.get(entity))). + clickHandler((panel, user1, clickType, slot) -> { + // On right click change which entities are selected for deletion. + if (clickType.isRightClick()) + { + if (!this.selectedEntities.add(entity)) + { + // Remove entity if it is already selected + this.selectedEntities.remove(entity); + } + + this.build(); + } + else + { + new NumberGUI(this.user, this.requiredEntities.get(entity), 1, (status, value) -> { + if (status) + { + // Update value only when something changes. + this.requiredEntities.put(entity, value); + } + + this.build(); + }); + } + return true; + }). + glow(this.selectedEntities.contains(entity)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * Functional buttons in current GUI. + */ + private enum Button + { + ADD, + REMOVE + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with entities to avoid list irregularities. + */ + private List entityList; + + /** + * Set with entities that are selected. + */ + private Set selectedEntities; + + /** + * Map that contains all entities and their cound. + */ + private Map requiredEntities; + + /** + * Boolean indicate if entities should be displayed as eggs or mob heads. + */ + private boolean asEggs; +} diff --git a/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java new file mode 100644 index 0000000..a1913f5 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/user/ChallengesGUI.java @@ -0,0 +1,557 @@ +package world.bentobox.challenges.panel.user; + + +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.ChallengesAddon; +import world.bentobox.challenges.ChallengesManager; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.panel.CommonGUI; +import world.bentobox.challenges.panel.TryToComplete; +import world.bentobox.challenges.utils.GuiUtils; +import world.bentobox.challenges.utils.LevelStatus; + + +/** + * This is UserGUI class. It contains everything necessary for user to use it. + */ +public class ChallengesGUI extends CommonGUI +{ +// --------------------------------------------------------------------- +// Section: Constructors +// --------------------------------------------------------------------- + + /** + * Default constructor that inits panels with minimal requirements, without parent panel. + * + * @param addon Addon where panel operates. + * @param world World from which panel was created. + * @param user User who created panel. + * @param topLabel Command top label which creates panel (f.e. island or ai) + * @param permissionPrefix Command permission prefix (f.e. bskyblock.) + */ + public ChallengesGUI(ChallengesAddon addon, + World world, + User user, + String topLabel, + String permissionPrefix) + { + super(addon, world, user, topLabel, permissionPrefix); + this.challengesManager = this.addon.getChallengesManager(); + + this.levelStatusList = this.challengesManager.getChallengeLevelStatus(this.user, this.world); + + for (LevelStatus levelStatus : this.levelStatusList) + { + if (levelStatus.isUnlocked()) + { + this.lastSelectedLevel = levelStatus; + } + else + { + break; + } + } + } + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + @Override + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation("challenges.gui.title")); + + // TODO: get last completed level. + + if (this.addon.getChallengesSettings().isFreeChallengesFirst()) + { + this.addFreeChallenges(panelBuilder); + + // Start new row for challenges. + while (panelBuilder.nextSlot() % 9 != 0) + { + panelBuilder.item( + new PanelItemBuilder().icon(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name(" ").build()); + } + } + + this.addChallenges(panelBuilder); + + // Start new row for levels. + while (panelBuilder.nextSlot() % 9 != 0) + { + panelBuilder.item( + new PanelItemBuilder().icon(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name(" ").build()); + } + + this.addChallengeLevels(panelBuilder); + + if (!this.addon.getChallengesSettings().isFreeChallengesFirst()) + { + // Start new row for free challenges. + while (panelBuilder.nextSlot() % 9 != 0) + { + panelBuilder.item( + new PanelItemBuilder().icon(Material.LIGHT_GRAY_STAINED_GLASS_PANE).name(" ").build()); + } + + this.addFreeChallenges(panelBuilder); + } + + panelBuilder.build(); + } + + + /** + * This method adds free challenges to panelBuilder. + * @param panelBuilder where free challenges must be added. + */ + private void addFreeChallenges(PanelBuilder panelBuilder) + { + List freeChallenges = this.challengesManager.getFreeChallenges(this.world); + final int freeChallengesCount = freeChallenges.size(); + + if (freeChallengesCount > 18) + { + int firstIndex = panelBuilder.nextSlot(); + + if (this.freeChallengeIndex > 0) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.previous")). + clickHandler((panel, user1, clickType, slot) -> { + this.freeChallengeIndex--; + this.build(); + return true; + }).build()); + } + + int currentIndex = this.freeChallengeIndex; + + while (panelBuilder.nextSlot() != firstIndex + 18 && currentIndex < freeChallengesCount) + { + panelBuilder.item(this.getChallengeButton(freeChallenges.get(currentIndex++))); + } + + // Check if one challenge is left + if (currentIndex + 1 == freeChallengesCount) + { + panelBuilder.item(this.getChallengeButton(freeChallenges.get(currentIndex))); + } + else if (currentIndex < freeChallengesCount) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.next")). + clickHandler((panel, user1, clickType, slot) -> { + this.freeChallengeIndex++; + this.build(); + return true; + }).build()); + } + } + else + { + for (Challenge challenge : freeChallenges) + { + // there are no limitations. Just bunch insert. + panelBuilder.item(this.getChallengeButton(challenge)); + } + } + } + + + /** + * This method adds last selected level challenges to panelBuilder. + * @param panelBuilder where last selected level challenges must be added. + */ + private void addChallenges(PanelBuilder panelBuilder) + { + if (this.lastSelectedLevel != null) + { + List challenges = this.challengesManager.getLevelChallenges(this.lastSelectedLevel.getLevel()); + final int challengesCount = challenges.size(); + + if (challengesCount > 18) + { + int firstIndex = panelBuilder.nextSlot(); + + if (this.pageIndex > 0) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.previous")). + clickHandler((panel, user1, clickType, slot) -> { + this.pageIndex--; + this.build(); + return true; + }).build()); + } + + int currentIndex = this.pageIndex; + + while (panelBuilder.nextSlot() != firstIndex + 18 && currentIndex < challengesCount) + { + panelBuilder.item(this.getChallengeButton(challenges.get(currentIndex++))); + } + + // Check if one challenge is left + if (currentIndex + 1 == challengesCount) + { + panelBuilder.item(this.getChallengeButton(challenges.get(currentIndex))); + } + else if (currentIndex < challengesCount) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.next")). + clickHandler((panel, user1, clickType, slot) -> { + this.pageIndex++; + this.build(); + return true; + }).build()); + } + } + else + { + for (Challenge challenge : challenges) + { + // there are no limitations. Just bunch insert. + panelBuilder.item(this.getChallengeButton(challenge)); + } + } + } + } + + + /** + * This method adds challenge levels to panelBuilder. + * @param panelBuilder where challenge levels must be added. + */ + private void addChallengeLevels(PanelBuilder panelBuilder) + { + // Clone to avoid creating new level on each build. + List leftLevels = new ArrayList<>(this.levelStatusList); + leftLevels.remove(this.lastSelectedLevel); + + // TODO: Focusing on middle should be awsome. + + final int levelCounts = leftLevels.size(); + + if (levelCounts > 9) + { + int firstIndex = panelBuilder.nextSlot(); + + if (this.levelIndex > 0) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.previous")). + clickHandler((panel, user1, clickType, slot) -> { + this.levelIndex--; + this.build(); + return true; + }).build()); + } + + int currentIndex = this.levelIndex; + + while (panelBuilder.nextSlot() != firstIndex + 9 && currentIndex < levelCounts) + { + panelBuilder.item(this.getLevelButton(leftLevels.get(currentIndex++))); + } + + // Check if one challenge is left + if (currentIndex + 1 == levelCounts) + { + panelBuilder.item(this.getLevelButton(leftLevels.get(currentIndex))); + } + else if (currentIndex < levelCounts) + { + panelBuilder.item(new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.next")). + clickHandler((panel, user1, clickType, slot) -> { + this.levelIndex++; + this.build(); + return true; + }).build()); + } + } + else + { + for (LevelStatus level : leftLevels) + { + // there are no limitations. Just bunch insert. + panelBuilder.item(this.getLevelButton(level)); + } + } + } + + +// --------------------------------------------------------------------- +// Section: Icon building +// --------------------------------------------------------------------- + + + /** + * This method creates given challenges icon that on press tries to complete it. + * @param challenge which icon must be constructed. + * @return PanelItem icon for challenge. + */ + private PanelItem getChallengeButton(Challenge challenge) + { + return new PanelItemBuilder(). + icon(challenge.getIcon()). + name(challenge.getFriendlyName().isEmpty() ? challenge.getUniqueId() : challenge.getFriendlyName()). + description(GuiUtils.stringSplit(this.createChallengeDescription(challenge))). + clickHandler((panel, user1, clickType, slot) -> { + new TryToComplete(this.addon, + this.user, + challenge, + this.world, + this.topLabel, + this.permissionPrefix); + return true; + }). + glow(this.challengesManager.isChallengeComplete(this.user, challenge)). + build(); + } + + + /** + * This method creates challenges description by adding all information that is necessary for this challenge. + * @param challenge Which information must be retrieved. + * @return List with strings that contains information about given challenge. + */ + private List createChallengeDescription(Challenge challenge) + { + List result = new ArrayList<>(); + + result.add(this.user.getTranslation("challenges.level", + "[level]", this.challengesManager.getLevel(challenge).getFriendlyName())); + + boolean completed = this.challengesManager.isChallengeComplete(this.user, challenge); + + if (completed) + { + result.add(this.user.getTranslation("challenges.complete")); + } + + if (challenge.isRepeatable()) + { + int maxTimes = challenge.getMaxTimes(); + long doneTimes = this.challengesManager.getChallengeTimes(this.user, challenge); + + if (maxTimes > 0) + { + if (doneTimes < maxTimes) + { + result.add(this.user.getTranslation("challenges.completed-times", + "[donetimes]", String.valueOf(doneTimes), + "[maxtimes]", String.valueOf(maxTimes))); + + // Change value to false, as max count not reached. + completed = false; + } + else + { + result.add(this.user.getTranslation("challenges.maxed-reached", + "[donetimes]", String.valueOf(doneTimes), + "[maxtimes]", String.valueOf(maxTimes))); + } + } + else + { + result.add(this.user.getTranslation("challenges.completed-times", + "[donetimes]", String.valueOf(doneTimes))); + + // Change value to false, as max count not reached. + completed = false; + } + } + + if (!completed) + { + result.addAll(challenge.getDescription()); + + if (challenge.getChallengeType().equals(Challenge.ChallengeType.INVENTORY)) + { + if (challenge.isTakeItems()) + { + result.add(this.user.getTranslation("challenges.item-take-warning")); + } + } + else if (challenge.getChallengeType().equals(Challenge.ChallengeType.ISLAND)) + { + result.add(this.user.getTranslation("challenges.items-closeby")); + + if (challenge.isRemoveEntities() && !challenge.getRequiredEntities().isEmpty()) + { + result.add(this.user.getTranslation("challenges.entities-kill-warning")); + } + + if (challenge.isRemoveBlocks() && !challenge.getRequiredBlocks().isEmpty()) + { + result.add(this.user.getTranslation("challenges.blocks-take-warning")); + } + } + } + + if (completed) + { + result.add(this.user.getTranslation("challenges.not-repeatable")); + } + else + { + result.addAll(this.challengeRewards(challenge)); + } + + result.replaceAll(x -> x.replace("[label]", this.topLabel)); + + return result; + } + + + /** + * This method returns list of strings that contains basic information about challenge rewards. + * @param challenge which reward message must be created. + * @return list of strings that contains rewards message. + */ + private List challengeRewards(Challenge challenge) + { + String rewardText; + double rewardMoney; + int rewardExperience; + + if (!this.challengesManager.isChallengeComplete(this.user, challenge)) + { + rewardText = challenge.getRewardText(); + rewardMoney = challenge.getRewardMoney(); + rewardExperience = challenge.getRewardExperience(); + } + else + { + rewardText = challenge.getRepeatRewardText(); + rewardMoney = challenge.getRepeatMoneyReward(); + rewardExperience = challenge.getRepeatExperienceReward(); + } + + List result = new ArrayList<>(); + + // Add reward text + result.add(rewardText); + + // Add message about reward XP + if (rewardExperience > 0) + { + result.add(this.user.getTranslation("challenges.exp-reward", + "[reward]", Integer.toString(rewardExperience))); + } + + // Add message about reward money + if (this.addon.getPlugin().getSettings().isUseEconomy() && rewardMoney > 0) + { + result.add(this.user.getTranslation("challenges.money-reward", + "[reward]", Double.toString(rewardMoney))); + } + + return result; + } + + + /** + * This method creates button for given level. + * @param level which button must be created. + * @return Button for given level. + */ + private PanelItem getLevelButton(LevelStatus level) + { + // Create a nice name for the level + String name = level.getLevel().getFriendlyName().isEmpty() ? + level.getLevel().getUniqueId() : + level.getLevel().getFriendlyName(); + + ItemStack icon; + List description; + PanelItem.ClickHandler clickHandler; + boolean glow; + + if (level.isUnlocked()) + { + icon = level.getLevel().getIcon(); + description = GuiUtils.stringSplit(this.user.getTranslation("challenges.navigation", "[level]", name)); + clickHandler = (panel, user1, clickType, slot) -> { + this.lastSelectedLevel = level; + + // Reset level and page index. + this.levelIndex = 0; + this.pageIndex = 0; + + this.build(); + return true; + }; + glow = this.challengesManager.isLevelCompleted(this.user, level.getLevel()); + } + else + { + icon = new ItemStack(Material.BOOK); + + description = GuiUtils.stringSplit( + this.user.getTranslation("challenges.to-complete", + "[challengesToDo]", Integer.toString(level.getNumberOfChallengesStillToDo()), + "[thisLevel]", level.getPreviousLevel().getFriendlyName())); + + clickHandler = null; + glow = false; + } + + return new PanelItem(icon, name, description, glow, clickHandler, false); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This will be used if free challenges are more then 18. + */ + private int freeChallengeIndex = 0; + + /** + * This will be used if levels are more then 9. + */ + private int levelIndex; + + /** + * This list contains all information about level completion in current world. + */ + private List levelStatusList; + + /** + * This indicate last selected level. + */ + private LevelStatus lastSelectedLevel; + + /** + * Challenge Manager object. + */ + private ChallengesManager challengesManager; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java new file mode 100644 index 0000000..3d36baf --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/ConfirmationGUI.java @@ -0,0 +1,114 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; + +import java.util.function.Consumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This GUI is used to confirm that user wants to run command, that should be created from + * command string list. + */ +public class ConfirmationGUI +{ + /** + * This constructor inits and opens ConfirmationGUI. + * + * @param user Gui Caller. + */ + public ConfirmationGUI(User user, Consumer consumer) + { + this.user = user; + this.consumer = consumer; + + this.build(); + } + + + /** + * This method builds confirmation panel with 2 buttons. + */ + public void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.admin.confirm-title")); + + GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + // Accept buttons + panelBuilder.item(10, this.getButton(true)); + panelBuilder.item(11, this.getButton(true)); + panelBuilder.item(12, this.getButton(true)); + + panelBuilder.item(19, this.getButton(true)); + panelBuilder.item(20, this.getButton(true)); + panelBuilder.item(21, this.getButton(true)); + + panelBuilder.item(28, this.getButton(true)); + panelBuilder.item(29, this.getButton(true)); + panelBuilder.item(30, this.getButton(true)); + + // Cancel Buttons + panelBuilder.item(14, this.getButton(false)); + panelBuilder.item(15, this.getButton(false)); + panelBuilder.item(16, this.getButton(false)); + + panelBuilder.item(23, this.getButton(false)); + panelBuilder.item(24, this.getButton(false)); + panelBuilder.item(25, this.getButton(false)); + + panelBuilder.item(32, this.getButton(false)); + panelBuilder.item(33, this.getButton(false)); + panelBuilder.item(34, this.getButton(false)); + + panelBuilder.item(44, + new PanelItemBuilder(). + icon(Material.OAK_DOOR). + name(this.user.getTranslation("challenges.gui.buttons.return")). + clickHandler( (panel, user1, clickType, slot) -> { + this.consumer.accept(false); + return true; + }).build()); + + panelBuilder.build(); + } + + + /** + * This method creates button with requested value. + * @param returnValue requested value + * @return PanelItem button. + */ + private PanelItem getButton(boolean returnValue) + { + return new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons." + (returnValue ? "accept" : "cancel"))). + icon(returnValue ? Material.GRAY_STAINED_GLASS_PANE : Material.RED_STAINED_GLASS_PANE). + clickHandler((panel, user1, clickType, i) -> { + this.consumer.accept(returnValue); + return true; + }). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * User who wants to run command. + */ + private User user; + + /** + * Stores current Consumer + */ + private Consumer consumer; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java b/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java new file mode 100644 index 0000000..2d44739 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/ItemSwitchGUI.java @@ -0,0 +1,236 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.PanelListener; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class allows to change Input ItemStacks to different ItemStacks. + */ +public class ItemSwitchGUI +{ + public ItemSwitchGUI(User user, List itemStacks, BiConsumer> consumer) + { + this.consumer = consumer; + this.user = user; + this.itemStacks = itemStacks; + this.build(); + } + + + /** + * This method builds panel that allows to change given number value. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.change-items")); + + // Size of inventory that user can set via GUI. + panelBuilder.size(45); + + panelBuilder.listener(new CustomPanelListener()); + + panelBuilder.item(0, this.getButton(Button.SAVE)); + + for (int i = 1; i < 8; i++) + { + panelBuilder.item(i, this.getButton(Button.EMPTY)); + } + + panelBuilder.item(8, this.getButton(Button.CANCEL)); + + for (ItemStack itemStack : this.itemStacks) + { + panelBuilder.item(new CustomPanelItem(itemStack)); + } + + panelBuilder.build().open(this.user); + } + + + /** + * This method create button that does some functionality in current gui. + * @param button Button functionality. + * @return PanelItem. + */ + private PanelItem getButton(Button button) + { + ItemStack icon; + String name; + List description; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case SAVE: + { + name = this.user.getTranslation("challenges.gui.buttons.save"); + description = Collections.emptyList(); + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> { + // Magic number 9 - second row. First row is for custom buttons. + // Magic number 45 - This GUI is initialed with 45 elements. + List returnItems = new ArrayList<>(36); + + for (int i = 9; i < 45; i++) + { + ItemStack itemStack = panel.getInventory().getItem(i); + + if (itemStack != null) + { + returnItems.add(itemStack); + } + } + + this.consumer.accept(true, returnItems); + + return true; + }; + break; + } + case CANCEL: + { + name = this.user.getTranslation("challenges.gui.buttons.cancel"); + description = Collections.emptyList(); + icon = new ItemStack(Material.IRON_DOOR); + clickHandler = (panel, user, clickType, slot) -> { + this.consumer.accept(false, Collections.emptyList()); + return true; + }; + break; + } + case EMPTY: + { + name = ""; + description = Collections.emptyList(); + icon = new ItemStack(Material.BARRIER); + clickHandler = (panel, user, clickType, slot) -> true; + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), false, clickHandler, false); + } + + +// --------------------------------------------------------------------- +// Section: Private classes +// --------------------------------------------------------------------- + + + /** + * This CustomPanelItem does no lose Item original MetaData. After PanelItem has been + * created it restores original meta data. It also does not allow to change anything that + * could destroy meta data. + */ + private class CustomPanelItem extends PanelItem + { + CustomPanelItem(ItemStack item) + { + super(item.clone(), "", Collections.emptyList(), false, null, false); + this.getItem().setItemMeta(item.getItemMeta()); + } + + + @Override + public void setGlow(boolean glow) + { + } + + + @Override + public void setDescription(List description) + { + } + + + @Override + public void setName(String name) + { + } + + + @Override + public void setHead(ItemStack itemStack) + { + } + } + + + /** + * This CustomPanelListener allows to move items in current panel. + */ + private class CustomPanelListener implements PanelListener + { + @Override + public void setup() + { + } + + + @Override + public void onInventoryClose(InventoryCloseEvent inventoryCloseEvent) + { + } + + + @Override + public void onInventoryClick(User user, InventoryClickEvent event) + { + // First row of elements should be ignored, as it contains buttons and blocked slots. + event.setCancelled(event.getRawSlot() < 9); + } + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * This enum holds all button values in current gui. + */ + private enum Button + { + CANCEL, + SAVE, + EMPTY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * User who opens current gui. + */ + private User user; + + /** + * List with original items. + */ + private List itemStacks; + + /** + * Consumer that returns item stacks on save action. + */ + private BiConsumer> consumer; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java b/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java new file mode 100644 index 0000000..6eb0376 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/NumberGUI.java @@ -0,0 +1,420 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +import net.wesjd.anvilgui.AnvilGUI; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This gui allows to change current number and returns it to previous GUI + */ +public class NumberGUI +{ + public NumberGUI(User user, int value, BiConsumer consumer) + { + this(user, value, Integer.MIN_VALUE, Integer.MAX_VALUE, consumer); + } + + + public NumberGUI(User user, int value, int minValue, BiConsumer consumer) + { + this(user, value, minValue, Integer.MAX_VALUE, consumer); + } + + + public NumberGUI(User user, int value, int minValue, int maxValue, BiConsumer consumer) + { + this.user = user; + this.value = value; + this.consumer = consumer; + + this.minValue = minValue; + this.maxValue = maxValue; + + this.currentOperation = Button.SET; + + this.build(); + } + + + /** + * This method builds panel that allows to change given number value. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.edit-number-title")); + + GuiUtils.fillBorder(panelBuilder); + + // Others + panelBuilder.item(1, this.getButton(Button.SAVE)); + + panelBuilder.item(19, this.getButton(Button.VALUE)); + panelBuilder.item(44, this.getButton(Button.CANCEL)); + + panelBuilder.item(2, this.getButton(Button.INPUT)); + + // operations + panelBuilder.item(3, this.getButton(Button.SET)); + panelBuilder.item(4, this.getButton(Button.INCREASE)); + panelBuilder.item(5, this.getButton(Button.REDUCE)); + panelBuilder.item(6, this.getButton(Button.MULTIPLY)); + + // Numbers + panelBuilder.item(11, this.createNumberButton(1)); + panelBuilder.item(12, this.createNumberButton(10)); + panelBuilder.item(13, this.createNumberButton(100)); + panelBuilder.item(14, this.createNumberButton(1000)); + panelBuilder.item(15, this.createNumberButton(10000)); + + panelBuilder.item(20, this.createNumberButton(2)); + panelBuilder.item(21, this.createNumberButton(20)); + panelBuilder.item(22, this.createNumberButton(200)); + panelBuilder.item(23, this.createNumberButton(2000)); + panelBuilder.item(24, this.createNumberButton(20000)); + + panelBuilder.item(29, this.createNumberButton(5)); + panelBuilder.item(30, this.createNumberButton(50)); + panelBuilder.item(31, this.createNumberButton(500)); + panelBuilder.item(32, this.createNumberButton(5000)); + panelBuilder.item(33, this.createNumberButton(50000)); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem with required functionality. + * @param button Functionality requirement. + * @return PanelItem with functionality. + */ + private PanelItem getButton(Button button) + { + ItemStack icon; + String name; + List description; + PanelItem.ClickHandler clickHandler; + boolean glow; + + switch (button) + { + case SAVE: + { + name = this.user.getTranslation("challenges.gui.buttons.save"); + description = Collections.emptyList(); + icon = new ItemStack(Material.COMMAND_BLOCK); + clickHandler = (panel, user, clickType, slot) -> { + this.consumer.accept(true, this.value); + return true; + }; + glow = false; + break; + } + case CANCEL: + { + name = this.user.getTranslation("challenges.gui.buttons.cancel"); + description = Collections.emptyList(); + icon = new ItemStack(Material.OAK_DOOR); + clickHandler = (panel, user, clickType, slot) -> { + this.consumer.accept(false, this.value); + return true; + }; + glow = false; + break; + } + case INPUT: + { + name = this.user.getTranslation("challenges.gui.buttons.input"); + description = Collections.emptyList(); + icon = new ItemStack(Material.ANVIL); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(BentoBox.getInstance(), + this.user.getPlayer(), + Integer.toString(this.value), + (player, reply) -> { + try + { + this.value = Integer.parseInt(reply); + + if (this.value > this.maxValue || this.value < this.minValue) + { + this.user.sendMessage("challenges.error.not-valid-integer", + "[value]", reply, + "[min]", Integer.toString(this.minValue), + "[max]", Integer.toString(this.maxValue)); + } + else + { + this.build(); + } + } + catch (Exception e) + { + reply = Integer.toString(this.value); + this.user.sendMessage("challenges.error.not-a-integer", "[value]", reply); + } + + return reply; + }); + + return true; + }; + glow = false; + break; + } + case VALUE: + { + name = this.user.getTranslation("challenges.gui.buttons.value"); + description = Collections.singletonList(Integer.toString(this.value)); + icon = new ItemStack(Material.PAPER); + clickHandler = (panel, user, clickType, slot) -> true; + glow = false; + break; + } + case SET: + { + name = this.user.getTranslation("challenges.gui.buttons.set"); + description = Collections.emptyList(); + icon = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentOperation = Button.SET; + this.build(); + return true; + }; + glow = this.currentOperation.equals(Button.SET); + break; + } + case INCREASE: + { + name = this.user.getTranslation("challenges.gui.buttons.increase"); + description = Collections.emptyList(); + icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentOperation = Button.INCREASE; + this.build(); + return true; + }; + glow = this.currentOperation.equals(Button.INCREASE); + break; + } + case REDUCE: + { + name = this.user.getTranslation("challenges.gui.buttons.reduce"); + description = Collections.emptyList(); + icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentOperation = Button.REDUCE; + this.build(); + return true; + }; + glow = this.currentOperation.equals(Button.REDUCE); + break; + } + case MULTIPLY: + { + name = this.user.getTranslation("challenges.gui.buttons.multiply"); + description = Collections.emptyList(); + icon = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.currentOperation = Button.MULTIPLY; + this.build(); + return true; + }; + glow = this.currentOperation.equals(Button.MULTIPLY); + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), glow, clickHandler, false); + } + + + /** + * This method creates Number Button based on input number. + * @param number Number which button must be created. + * @return PanelItem that represents number button. + */ + private PanelItem createNumberButton(int number) + { + PanelItemBuilder itemBuilder = new PanelItemBuilder(); + + switch (this.currentOperation) + { + case SET: + { + itemBuilder.name(this.user.getTranslation("biomes.gui.buttons.set","[number]", Integer.toString(number))); + itemBuilder.icon(Material.WHITE_STAINED_GLASS_PANE); + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + this.value = number; + + if (this.value > this.maxValue) + { + this.user.sendMessage("challenges.error.not-valid-integer", + "[value]", Integer.toString(this.value), + "[min]", Integer.toString(this.minValue), + "[max]", Integer.toString(this.maxValue)); + + this.value = this.maxValue; + } + + if (this.value < this.minValue) + { + this.user.sendMessage("challenges.error.not-valid-integer", + "[value]", Integer.toString(this.value), + "[min]", Integer.toString(this.minValue), + "[max]", Integer.toString(this.maxValue)); + + this.value = this.minValue; + } + + this.build(); + return true; + }); + + break; + } + case INCREASE: + { + itemBuilder.name(this.user.getTranslation("biomes.gui.buttons.increase","[number]", Integer.toString(number))); + itemBuilder.icon(Material.GREEN_STAINED_GLASS_PANE); + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + this.value += number; + + if (this.value > this.maxValue) + { + this.user.sendMessage("challenges.error.not-valid-integer", + "[value]", Integer.toString(this.value), + "[min]", Integer.toString(this.minValue), + "[max]", Integer.toString(this.maxValue)); + + this.value = this.maxValue; + } + + this.build(); + return true; + }); + + break; + } + case REDUCE: + { + itemBuilder.name(this.user.getTranslation("biomes.gui.buttons.reduce","[number]", Integer.toString(number))); + itemBuilder.icon(Material.RED_STAINED_GLASS_PANE); + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + this.value -= number; + + if (this.value < this.minValue) + { + this.user.sendMessage("challenges.error.not-valid-integer", + "[value]", Integer.toString(this.value), + "[min]", Integer.toString(this.minValue), + "[max]", Integer.toString(this.maxValue)); + + this.value = this.minValue; + } + + this.build(); + return true; + }); + + break; + } + case MULTIPLY: + { + itemBuilder.name(this.user.getTranslation("biomes.gui.buttons.multiply","[number]", Integer.toString(number))); + itemBuilder.icon(Material.BLUE_STAINED_GLASS_PANE); + itemBuilder.clickHandler((panel, user1, clickType, i) -> { + this.value *= number; + + if (this.value > this.maxValue) + { + this.user.sendMessage("challenges.error.not-valid-integer", + "[value]", Integer.toString(this.value), + "[min]", Integer.toString(this.minValue), + "[max]", Integer.toString(this.maxValue)); + + this.value = this.maxValue; + } + + this.build(); + return true; + }); + + break; + } + } + + return itemBuilder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * This enum contains all button types. + */ + private enum Button + { + SAVE, + CANCEL, + INPUT, + + VALUE, + + SET, + INCREASE, + REDUCE, + MULTIPLY + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This variable stores current GUI consumer. + */ + private BiConsumer consumer; + + /** + * User who runs GUI. + */ + private User user; + + /** + * Current value. + */ + private int value; + + /** + * Minimal value that is allowed to set. + */ + private int minValue; + + /** + * Maximal value that is allowed to set. + */ + private int maxValue; + + /** + * This variable holds which operation now is processed. + */ + private Button currentOperation; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java new file mode 100644 index 0000000..4870d04 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectBlocksGUI.java @@ -0,0 +1,195 @@ +package world.bentobox.challenges.panel.util; + + +import org.apache.commons.lang.WordUtils; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class contains all necessary things that allows to select single block from all ingame blocks. Selected + * block will be returned via BiConsumer. + */ +public class SelectBlocksGUI +{ + public SelectBlocksGUI(User user, BiConsumer consumer) + { + this(user, Collections.emptySet(), consumer); + } + + + public SelectBlocksGUI(User user, Set excludedMaterial, BiConsumer consumer) + { + this.consumer = consumer; + this.user = user; + + // Current GUI cannot display air blocks. It crashes with null-pointer + excludedMaterial.add(Material.AIR); + excludedMaterial.add(Material.CAVE_AIR); + excludedMaterial.add(Material.VOID_AIR); + + // Piston head and moving piston is not necessary. useless. + excludedMaterial.add(Material.PISTON_HEAD); + excludedMaterial.add(Material.MOVING_PISTON); + + // Barrier cannot be accessible to user. + excludedMaterial.add(Material.BARRIER); + + this.elements = new ArrayList<>(); + + for (Material material : Material.values()) + { + if (material.isBlock() && !material.isLegacy() && !excludedMaterial.contains(material)) + { + this.elements.add(material); + } + } + + this.build(0); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds all necessary elements in GUI panel. + */ + public void build(int pageIndex) + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation("challenges.gui.admin.select-block")); + + GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + final int MAX_ELEMENTS = 21; + final int correctPage; + + if (pageIndex < 0) + { + correctPage = this.elements.size() / MAX_ELEMENTS; + } + else if (pageIndex > (this.elements.size() / MAX_ELEMENTS)) + { + correctPage = 0; + } + else + { + correctPage = pageIndex; + } + + int entitiesIndex = MAX_ELEMENTS * correctPage; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (entitiesIndex < ((correctPage + 1) * MAX_ELEMENTS) && + entitiesIndex < this.elements.size()) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, this.createMaterialButton(this.elements.get(entitiesIndex++))); + } + + index++; + } + + panelBuilder.item(4, + new PanelItemBuilder(). + icon(Material.RED_STAINED_GLASS_PANE). + name(this.user.getTranslation("challenges.gui.buttons.cancel")). + clickHandler( (panel, user1, clickType, slot) -> { + this.consumer.accept(false, null); + return true; + }).build()); + + if (this.elements.size() > MAX_ELEMENTS) + { + // Navigation buttons if necessary + + panelBuilder.item(18, + new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.previous")). + clickHandler((panel, user1, clickType, slot) -> { + this.build(correctPage - 1); + return true; + }).build()); + + panelBuilder.item(26, + new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.next")). + clickHandler((panel, user1, clickType, slot) -> { + this.build(correctPage + 1); + return true; + }).build()); + } + + panelBuilder.item(44, + new PanelItemBuilder(). + icon(Material.OAK_DOOR). + name(this.user.getTranslation("challenges.gui.buttons.return")). + clickHandler( (panel, user1, clickType, slot) -> { + this.consumer.accept(false, null); + return true; + }).build()); + + panelBuilder.build(); + } + + + /** + * This method creates PanelItem that represents given material. + * Some materials is not displayable in Inventory GUI, so they are replaced with "placeholder" items. + * @param material Material which icon must be created. + * @return PanelItem that represents given material. + */ + private PanelItem createMaterialButton(Material material) + { + ItemStack itemStack = GuiUtils.getMaterialItem(material); + + return new PanelItemBuilder(). + name(WordUtils.capitalize(material.name().toLowerCase().replace("_", " "))). + icon(itemStack). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, material); + return true; + }). + glow(!itemStack.getType().equals(material)). + build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * List with elements that will be displayed in current GUI. + */ + private List elements; + + /** + * This variable stores consumer. + */ + private BiConsumer consumer; + + /** + * User who runs GUI. + */ + private User user; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java new file mode 100644 index 0000000..ab6bdab --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectChallengeGUI.java @@ -0,0 +1,158 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import java.util.List; +import java.util.function.BiConsumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class creates new GUI that allows to select single challenge, which is returned via consumer. + */ +public class SelectChallengeGUI +{ + public SelectChallengeGUI(User user, List challengesList, BiConsumer consumer) + { + this.consumer = consumer; + this.user = user; + this.challengesList = challengesList; + + this.build(0); + } + + + /** + * This method builds panel that allows to select single challenge from input challenges. + */ + private void build(int pageIndex) + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.choose-challenge-title")); + + GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + // Maximal elements in page. + final int MAX_ELEMENTS = 21; + + final int correctPage; + + if (pageIndex < 0) + { + correctPage = this.challengesList.size() / MAX_ELEMENTS; + } + else if (pageIndex > (this.challengesList.size() / MAX_ELEMENTS)) + { + correctPage = 0; + } + else + { + correctPage = pageIndex; + } + + panelBuilder.item(4, + new PanelItemBuilder(). + icon(Material.RED_STAINED_GLASS_PANE). + name(this.user.getTranslation("challenges.gui.buttons.return")). + clickHandler( (panel, user1, clickType, slot) -> { + this.consumer.accept(false, null); + return true; + }).build()); + + if (this.challengesList.size() > MAX_ELEMENTS) + { + // Navigation buttons if necessary + + panelBuilder.item(18, + new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.previous")). + clickHandler((panel, user1, clickType, slot) -> { + this.build(correctPage - 1); + return true; + }).build()); + + panelBuilder.item(26, + new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.next")). + clickHandler((panel, user1, clickType, slot) -> { + this.build(correctPage + 1); + return true; + }).build()); + } + + int challengesIndex = MAX_ELEMENTS * correctPage; + + // I want first row to be only for navigation and return button. + int index = 10; + + while (challengesIndex < ((correctPage + 1) * MAX_ELEMENTS) && + challengesIndex < this.challengesList.size() && + index < 36) + { + if (!panelBuilder.slotOccupied(index)) + { + panelBuilder.item(index, + this.createChallengeButton(this.challengesList.get(challengesIndex++))); + } + + index++; + } + + panelBuilder.item(44, + new PanelItemBuilder(). + icon(Material.OAK_DOOR). + name(this.user.getTranslation("challenges.gui.buttons.return")). + clickHandler( (panel, user1, clickType, slot) -> { + this.consumer.accept(false, null); + return true; + }).build()); + + panelBuilder.build(); + } + + + /** + * This method builds PanelItem for given challenge. + * @param challenge Challenge which PanelItem must be created. + * @return new PanelItem for given Challenge. + */ + private PanelItem createChallengeButton(Challenge challenge) + { + return new PanelItemBuilder(). + name(challenge.getFriendlyName()). + description(GuiUtils.stringSplit(challenge.getDescription())). + icon(challenge.getIcon()). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, challenge); + return true; + }).build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores consumer. + */ + private BiConsumer consumer; + + /** + * User who runs GUI. + */ + private User user; + + /** + * Current value. + */ + private List challengesList; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java new file mode 100644 index 0000000..31114c3 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectEntityGUI.java @@ -0,0 +1,190 @@ +package world.bentobox.challenges.panel.util; + + +import org.apache.commons.lang.WordUtils; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.*; +import java.util.function.BiConsumer; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This GUI allows to select single entity and return it via Consumer. + */ +public class SelectEntityGUI +{ + public SelectEntityGUI(User user, BiConsumer consumer) + { + this(user, Collections.emptySet(), true, consumer); + } + + + public SelectEntityGUI(User user, Set excludedEntities, boolean asEggs, BiConsumer consumer) + { + this.consumer = consumer; + this.user = user; + this.asEggs = asEggs; + + this.entities = new ArrayList<>(EntityType.values().length); + + for (EntityType entityType : EntityType.values()) + { + if (entityType.isAlive() && !excludedEntities.contains(entityType)) + { + this.entities.add(entityType); + } + } + + // Sort mobs by their name for easier search. + this.entities.sort(Comparator.comparing(Enum::name)); + + this.build(0); + } + + +// --------------------------------------------------------------------- +// Section: Methods +// --------------------------------------------------------------------- + + + /** + * This method builds + */ + private void build(int pageIndex) + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.choose-entity-title")); + + GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + // Maximal elements in page. + final int MAX_ELEMENTS = 21; + + final int correctPage; + + if (pageIndex < 0) + { + correctPage = this.entities.size() / MAX_ELEMENTS; + } + else if (pageIndex > (this.entities.size() / MAX_ELEMENTS)) + { + correctPage = 0; + } + else + { + correctPage = pageIndex; + } + + panelBuilder.item(4, + new PanelItemBuilder(). + icon(Material.RED_STAINED_GLASS_PANE). + name(this.user.getTranslation("challenges.gui.buttons.cancel")). + clickHandler( (panel, user1, clickType, slot) -> { + this.consumer.accept(false, null); + return true; + }).build()); + + if (this.entities.size() > MAX_ELEMENTS) + { + // Navigation buttons if necessary + + panelBuilder.item(18, + new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.previous")). + clickHandler((panel, user1, clickType, slot) -> { + this.build(correctPage - 1); + return true; + }).build()); + + panelBuilder.item(26, + new PanelItemBuilder(). + icon(Material.SIGN). + name(this.user.getTranslation("challenges.gui.buttons.next")). + clickHandler((panel, user1, clickType, slot) -> { + this.build(correctPage + 1); + return true; + }).build()); + } + + int entitiesIndex = MAX_ELEMENTS * correctPage; + + // I want first row to be only for navigation and return button. + int slot = 10; + + while (entitiesIndex < ((correctPage + 1) * MAX_ELEMENTS) && + entitiesIndex < this.entities.size() && + slot < 36) + { + if (!panelBuilder.slotOccupied(slot)) + { + panelBuilder.item(slot, + this.createEntityButton(this.entities.get(entitiesIndex++))); + } + + slot++; + } + + panelBuilder.item(44, + new PanelItemBuilder(). + icon(Material.OAK_DOOR). + name(this.user.getTranslation("challenges.gui.buttons.return")). + clickHandler( (panel, user1, clickType, i) -> { + this.consumer.accept(false, null); + return true; + }).build()); + + panelBuilder.build(); + } + + + /** + * This method builds PanelItem for given entity. + * @param entity Entity which PanelItem must be created. + * @return new PanelItem for given Entity. + */ + private PanelItem createEntityButton(EntityType entity) + { + ItemStack itemStack = this.asEggs ? GuiUtils.getEntityEgg(entity) : GuiUtils.getEntityHead(entity); + + return new PanelItemBuilder(). + name(WordUtils.capitalize(entity.name().toLowerCase().replace("_", " "))). + icon(itemStack). + clickHandler((panel, user1, clickType, slot) -> { + this.consumer.accept(true, entity); + return true; + }).build(); + } + + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * This variable stores consumer. + */ + private BiConsumer consumer; + + /** + * User who runs GUI. + */ + private User user; + + /** + * This variable stores if mobs must be displayed as Eggs "true" or Heads "false". + */ + private boolean asEggs; + + /** + * Entities that must be in list. + */ + private List entities; +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java b/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java new file mode 100644 index 0000000..a0a42e8 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/SelectEnvironmentGUI.java @@ -0,0 +1,147 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.World; +import java.util.Collections; +import java.util.Set; +import java.util.function.BiConsumer; + +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This class creates panel that allows to select and deselect World Environments. On save it runs + * input consumer with true and selected values. + */ +public class SelectEnvironmentGUI +{ + public SelectEnvironmentGUI(User user, Set values, BiConsumer> consumer) + { + this.user = user; + this.values = values; + this.consumer = consumer; + + this.build(); + } + + + /** + * This method builds environment select panel. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user).name(this.user.getTranslation("challenges.gui.admin.environment-title")); + + GuiUtils.fillBorder(panelBuilder, Material.BLUE_STAINED_GLASS_PANE); + + panelBuilder.item(3, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.save")). + icon(Material.GREEN_STAINED_GLASS_PANE). + clickHandler((panel, user1, clickType, index) -> { + this.consumer.accept(true, this.values); + return true; + }). + build()); + + panelBuilder.item(5, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.cancel")). + icon(Material.RED_STAINED_GLASS_PANE). + clickHandler((panel, user1, clickType, i) -> { + this.consumer.accept(false, Collections.emptySet()); + return true; + }). + build()); + + panelBuilder.item(20, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.nether")). + icon(Material.NETHERRACK). + clickHandler((panel, user1, clickType, i) -> { + if (this.values.contains(World.Environment.NETHER)) + { + this.values.remove(World.Environment.NETHER); + } + else + { + this.values.add(World.Environment.NETHER); + } + + this.build(); + return true; + }). + glow(this.values.contains(World.Environment.NETHER)). + build()); + panelBuilder.item(22, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.normal")). + icon(Material.DIRT). + clickHandler((panel, user1, clickType, i) -> { + if (this.values.contains(World.Environment.NORMAL)) + { + this.values.remove(World.Environment.NORMAL); + } + else + { + this.values.add(World.Environment.NORMAL); + } + + this.build(); + return true; + }). + glow(this.values.contains(World.Environment.NORMAL)). + build()); + panelBuilder.item(24, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.end")). + icon(Material.END_STONE). + clickHandler((panel, user1, clickType, i) -> { + if (this.values.contains(World.Environment.THE_END)) + { + this.values.remove(World.Environment.THE_END); + } + else + { + this.values.add(World.Environment.THE_END); + } + + this.build(); + return true; + }). + glow(this.values.contains(World.Environment.THE_END)). + build()); + + + panelBuilder.item(44, new PanelItemBuilder(). + name(this.user.getTranslation("challenges.gui.admin.buttons.return")). + icon(Material.OAK_DOOR). + clickHandler((panel, user1, clickType, i) -> { + this.consumer.accept(false, Collections.emptySet()); + return true; + }). + build()); + + panelBuilder.build(); + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * User who wants to run command. + */ + private User user; + + /** + * List with selected environments. + */ + private Set values; + + /** + * Stores current Consumer + */ + private BiConsumer> consumer; + +} diff --git a/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java b/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java new file mode 100644 index 0000000..964d531 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/panel/util/StringListGUI.java @@ -0,0 +1,246 @@ +package world.bentobox.challenges.panel.util; + + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; + +import net.wesjd.anvilgui.AnvilGUI; +import world.bentobox.bentobox.BentoBox; +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.challenges.utils.GuiUtils; + + +/** + * This GUI allows to edit List of strings. AnvilGUI has limited text space, so splitting + * text in multiple rows allows to edit each row separately. + */ +public class StringListGUI +{ + public StringListGUI(User user, Collection value, BiConsumer> consumer) + { + this(user, new ArrayList<>(value), consumer); + } + + + public StringListGUI(User user, List value, BiConsumer> consumer) + { + this.consumer = consumer; + this.user = user; + this.value = value; + + if (this.value.size() > 21) + { + // TODO: throw error that so large list cannot be edited. + this.consumer.accept(false, this.value); + } + else + { + this.build(); + } + } + + + /** + * This method builds panel that allows to change given string value. + */ + private void build() + { + PanelBuilder panelBuilder = new PanelBuilder().user(this.user). + name(this.user.getTranslation("challenges.gui.text-edit-title")); + + GuiUtils.fillBorder(panelBuilder, Material.BLACK_STAINED_GLASS_PANE); + + panelBuilder.item(1, this.getButton(Button.SAVE)); + panelBuilder.item(2, this.getButton(Button.VALUE)); + + panelBuilder.item(4, this.getButton(Button.ADD)); + panelBuilder.item(5, this.getButton(Button.REMOVE)); + panelBuilder.item(6, this.getButton(Button.CLEAR)); + + panelBuilder.item(44, this.getButton(Button.CANCEL)); + + int slot = 10; + + for (int stringIndex = 0; stringIndex < this.value.size() && slot < 36; stringIndex++) + { + if (!panelBuilder.slotOccupied(slot)) + { + panelBuilder.item(slot, + this.createStringElement(this.value.get(stringIndex), stringIndex)); + } + + slot++; + } + + panelBuilder.build(); + } + + + /** + * This method create button that does some functionality in current gui. + * @param button Button functionality. + * @return PanelItem. + */ + private PanelItem getButton(Button button) + { + ItemStack icon; + String name; + List description; + PanelItem.ClickHandler clickHandler; + + switch (button) + { + case SAVE: + { + name = this.user.getTranslation("challenges.gui.buttons.save"); + description = Collections.emptyList(); + icon = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.consumer.accept(true, this.value); + + return true; + }; + break; + } + case CANCEL: + { + name = this.user.getTranslation("challenges.gui.buttons.cancel"); + description = Collections.emptyList(); + icon = new ItemStack(Material.OAK_DOOR); + clickHandler = (panel, user, clickType, slot) -> { + this.consumer.accept(false, this.value); + + return true; + }; + break; + } + case VALUE: + { + name = this.user.getTranslation("challenges.gui.buttons.value"); + description = this.value; + icon = new ItemStack(Material.PAPER); + clickHandler = (panel, user, clickType, slot) -> true; + break; + } + case ADD: + { + name = this.user.getTranslation("challenges.gui.buttons.add"); + description = Collections.emptyList(); + icon = new ItemStack(Material.WHITE_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + new AnvilGUI(BentoBox.getInstance(), + this.user.getPlayer(), + " ", + (player, reply) -> { + this.value.add(reply); + this.build(); + return reply; + }); + return true; + }; + break; + } + case CLEAR: + { + name = this.user.getTranslation("challenges.gui.buttons.clear"); + description = Collections.emptyList(); + icon = new ItemStack(Material.RED_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.value.clear(); + this.build(); + return true; + }; + break; + } + case REMOVE: + { + name = this.user.getTranslation("challenges.gui.buttons.remove"); + description = Collections.emptyList(); + icon = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); + clickHandler = (panel, user, clickType, slot) -> { + this.value.removeIf(String::isEmpty); + + this.build(); + return true; + }; + break; + } + default: + return null; + } + + return new PanelItem(icon, name, GuiUtils.stringSplit(description), false, clickHandler, false); + } + + + /** + * This method creates paper icon that represents single line from list. + * @param element Paper Icon name + * @return PanelItem. + */ + private PanelItem createStringElement(String element, int stringIndex) + { + return new PanelItemBuilder(). + name(element). + icon(Material.PAPER). + clickHandler((panel, user1, clickType, i) -> { + new AnvilGUI(BentoBox.getInstance(), + this.user.getPlayer(), + element, + (player, reply) -> { + this.value.set(stringIndex, reply); + this.build(); + return reply; + }); + return true; + }).build(); + } + + +// --------------------------------------------------------------------- +// Section: Enums +// --------------------------------------------------------------------- + + + /** + * This enum holds all button values in current gui. + */ + private enum Button + { + VALUE, + ADD, + REMOVE, + CANCEL, + CLEAR, + SAVE + } + + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + + /** + * This variable stores consumer. + */ + private BiConsumer> consumer; + + /** + * User who runs GUI. + */ + private User user; + + /** + * Current value. + */ + private List value; +} diff --git a/src/main/java/world/bentobox/challenges/utils/GuiUtils.java b/src/main/java/world/bentobox/challenges/utils/GuiUtils.java new file mode 100644 index 0000000..737f735 --- /dev/null +++ b/src/main/java/world/bentobox/challenges/utils/GuiUtils.java @@ -0,0 +1,407 @@ +package world.bentobox.challenges.utils; + + +import org.apache.commons.lang.WordUtils; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import world.bentobox.bentobox.api.panels.PanelItem; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; + + +/** + * This class contains static methods that is used through multiple GUIs. + */ +public class GuiUtils +{ +// --------------------------------------------------------------------- +// Section: Border around GUIs +// --------------------------------------------------------------------- + + + /** + * This method creates border of black panes around given panel with 5 rows. + * @param panelBuilder PanelBuilder which must be filled with border blocks. + */ + public static void fillBorder(PanelBuilder panelBuilder) + { + GuiUtils.fillBorder(panelBuilder, 5, Material.BLACK_STAINED_GLASS_PANE); + } + + + /** + * This method sets black stained glass pane around Panel with given row count. + * @param panelBuilder object that builds Panel. + * @param rowCount in Panel. + */ + public static void fillBorder(PanelBuilder panelBuilder, int rowCount) + { + GuiUtils.fillBorder(panelBuilder, rowCount, Material.BLACK_STAINED_GLASS_PANE); + } + + + /** + * This method sets blocks with given Material around Panel with 5 rows. + * @param panelBuilder object that builds Panel. + * @param material that will be around Panel. + */ + public static void fillBorder(PanelBuilder panelBuilder, Material material) + { + GuiUtils.fillBorder(panelBuilder, 5, material); + } + + + /** + * This method sets blocks with given Material around Panel with given row count. + * @param panelBuilder object that builds Panel. + * @param rowCount in Panel. + * @param material that will be around Panel. + */ + public static void fillBorder(PanelBuilder panelBuilder, int rowCount, Material material) + { + // Only for useful filling. + if (rowCount < 3) + { + return; + } + + for (int i = 0; i < 9 * rowCount; i++) + { + // First (i < 9) and last (i > 35) rows must be filled + // First column (i % 9 == 0) and last column (i % 9 == 8) also must be filled. + + if (i < 9 || i > 9 * (rowCount - 1) || i % 9 == 0 || i % 9 == 8) + { + panelBuilder.item(i, BorderBlock.getPanelBorder(material)); + } + } + } + + +// --------------------------------------------------------------------- +// Section: ItemStack transformations +// --------------------------------------------------------------------- + + /** + * This method transforms entity into egg or block that corresponds given entity. + * If entity egg is not found, then it is replaced by block that represents entity or + * barrier block. + * @param entity Entity which egg must be returned. + * @return ItemStack that may be egg for given entity. + */ + public static ItemStack getEntityEgg(EntityType entity) + { + return GuiUtils.getEntityEgg(entity, 1); + } + + + /** + * This method transforms entity into egg or block that corresponds given entity. + * If entity egg is not found, then it is replaced by block that represents entity or + * barrier block. + * @param entity Entity which egg must be returned. + * @param amount Amount of ItemStack elements. + * @return ItemStack that may be egg for given entity. + */ + public static ItemStack getEntityEgg(EntityType entity, int amount) + { + ItemStack itemStack; + + switch (entity) + { + case PIG_ZOMBIE: + itemStack = new ItemStack(Material.ZOMBIE_PIGMAN_SPAWN_EGG); + break; + case ENDER_DRAGON: + itemStack = new ItemStack(Material.DRAGON_EGG); + break; + case WITHER: + itemStack = new ItemStack(Material.SOUL_SAND); + break; + case PLAYER: + itemStack = new ItemStack(Material.PLAYER_HEAD); + break; + case MUSHROOM_COW: + itemStack = new ItemStack(Material.MOOSHROOM_SPAWN_EGG); + break; + case SNOWMAN: + itemStack = new ItemStack(Material.CARVED_PUMPKIN); + break; + case IRON_GOLEM: + itemStack = new ItemStack(Material.IRON_BLOCK); + break; + case ARMOR_STAND: + itemStack = new ItemStack(Material.ARMOR_STAND); + break; + default: + Material material = Material.getMaterial(entity.name() + "_SPAWN_EGG"); + + if (material == null) + { + itemStack = new ItemStack(Material.BARRIER); + } + else + { + itemStack = new ItemStack(material); + } + + break; + } + + itemStack.setAmount(amount); + + return itemStack; + } + + + /** + * This method transforms entity into player head with skin that corresponds given + * entity. If entity head is not found, then it is replaced by barrier block. + * @param entity Entity which head must be returned. + * @return ItemStack that may be head for given entity. + */ + public static ItemStack getEntityHead(EntityType entity) + { + return GuiUtils.getEntityHead(entity, 1); + } + + + /** + * This method transforms entity into player head with skin that corresponds given + * entity. If entity head is not found, then it is replaced by barrier block. + * @param entity Entity which head must be returned. + * @param amount Amount of ItemStack elements. + * @return ItemStack that may be head for given entity. + */ + public static ItemStack getEntityHead(EntityType entity, int amount) + { + ItemStack itemStack; + + switch (entity) + { + case PLAYER: + itemStack = new ItemStack(Material.PLAYER_HEAD); + break; + case WITHER_SKELETON: + itemStack = new ItemStack(Material.WITHER_SKELETON_SKULL); + break; + case ARMOR_STAND: + itemStack = new ItemStack(Material.ARMOR_STAND); + break; + case SKELETON: + itemStack = new ItemStack(Material.SKELETON_SKULL); + break; + case GIANT: + case ZOMBIE: + itemStack = new ItemStack(Material.ZOMBIE_HEAD); + break; + case CREEPER: + itemStack = new ItemStack(Material.CREEPER_HEAD); + break; + case ENDER_DRAGON: + itemStack = new ItemStack(Material.DRAGON_HEAD); + break; + default: + HeadLib head = HeadLib.getHead(entity.name()); + + if (head == null) + { + itemStack = new ItemStack(Material.BARRIER); + } + else + { + itemStack = head.toItemStack(); + } + break; + } + + itemStack.setAmount(amount); + + return itemStack; + } + + + /** + * This method transforms material into item stack that can be displayed in users + * inventory. + * @param material Material which item stack must be returned. + * @return ItemStack that represents given material. + */ + public static ItemStack getMaterialItem(Material material) + { + return GuiUtils.getMaterialItem(material, 1); + } + + + /** + * This method transforms material into item stack that can be displayed in users + * inventory. + * @param material Material which item stack must be returned. + * @param amount Amount of ItemStack elements. + * @return ItemStack that represents given material. + */ + public static ItemStack getMaterialItem(Material material, int amount) + { + ItemStack itemStack; + + // Process items that cannot be item-stacks. + if (material.name().contains("WALL_")) + { + // Materials that is attached to wall cannot be showed in GUI. But they should be in list. + itemStack = new ItemStack(Material.getMaterial(material.name().replace("WALL_", ""))); + } + else if (material.name().startsWith("POTTED_")) + { + // Materials Potted elements cannot be in inventory. + itemStack = new ItemStack(Material.getMaterial(material.name().replace("POTTED_", ""))); + } + else if (material.equals(Material.MELON_STEM) || material.equals(Material.ATTACHED_MELON_STEM)) + { + itemStack = new ItemStack(Material.MELON_SEEDS); + } + else if (material.equals(Material.PUMPKIN_STEM) || material.equals(Material.ATTACHED_PUMPKIN_STEM)) + { + itemStack = new ItemStack(Material.PUMPKIN_SEEDS); + } + else if (material.equals(Material.TALL_SEAGRASS)) + { + itemStack = new ItemStack(Material.SEAGRASS); + } + else if (material.equals(Material.CARROTS)) + { + itemStack = new ItemStack(Material.CARROT); + } + else if (material.equals(Material.BEETROOTS)) + { + itemStack = new ItemStack(Material.BEETROOT); + } + else if (material.equals(Material.POTATOES)) + { + itemStack = new ItemStack(Material.POTATO); + } + else if (material.equals(Material.COCOA)) + { + itemStack = new ItemStack(Material.COCOA_BEANS); + } + else if (material.equals(Material.KELP_PLANT)) + { + itemStack = new ItemStack(Material.KELP); + } + else if (material.equals(Material.REDSTONE_WIRE)) + { + itemStack = new ItemStack(Material.REDSTONE); + } + else if (material.equals(Material.TRIPWIRE)) + { + itemStack = new ItemStack(Material.STRING); + } + else if (material.equals(Material.FROSTED_ICE)) + { + itemStack = new ItemStack(Material.ICE); + } + else if (material.equals(Material.END_PORTAL) || material.equals(Material.END_GATEWAY) || material.equals(Material.NETHER_PORTAL)) + { + itemStack = new ItemStack(Material.PAPER); + } + else if (material.equals(Material.BUBBLE_COLUMN) || material.equals(Material.WATER)) + { + itemStack = new ItemStack(Material.WATER_BUCKET); + } + else if (material.equals(Material.LAVA)) + { + itemStack = new ItemStack(Material.LAVA_BUCKET); + } + else if (material.equals(Material.FIRE)) + { + itemStack = new ItemStack(Material.FIRE_CHARGE); + } + else if (material.equals(Material.AIR) || material.equals(Material.CAVE_AIR) || material.equals(Material.VOID_AIR)) + { + itemStack = new ItemStack(Material.GLASS_BOTTLE); + } + else if (material.equals(Material.PISTON_HEAD) || material.equals(Material.MOVING_PISTON)) + { + itemStack = new ItemStack(Material.PISTON); + } + else + { + itemStack = new ItemStack(material); + } + + itemStack.setAmount(amount); + + return itemStack; + } + + + /** + * This BorderBlock is simple PanelItem but without item meta data. + */ + private static class BorderBlock extends PanelItem + { + private BorderBlock(ItemStack icon) + { + super(icon.clone(), " ", Collections.emptyList(), false, null, false); + } + + + /** + * This method retunrs BorderBlock with requested item stack. + * @param material of which broder must be created. + * @return PanelItem that acts like border. + */ + private static BorderBlock getPanelBorder(Material material) + { + ItemStack itemStack = new ItemStack(material); + itemStack.getItemMeta().setDisplayName(" "); + + return new BorderBlock(itemStack); + } + } + + + /** + * Simple splitter + * + * @param string - string to be split + * @return list of split strings + */ + public static List stringSplit(String string) + { + // Remove all ending lines from string. + string = string.replaceAll("([\\r\\n])", "\\|"); + string = ChatColor.translateAlternateColorCodes('&', string); + // Check length of lines + List result = new ArrayList<>(); + + Arrays.stream(string.split("\\|")). + map(line -> Arrays.asList(WordUtils.wrap(line, 25).split("\\r\\n"))). + forEach(result::addAll); + + return result; + } + + + /** + * Simple splitter for all strings in list. + * @param stringList - list of string to be split + * @return list of split strings + */ + public static List stringSplit(List stringList) + { + if (stringList.isEmpty()) + { + return stringList; + } + + List newList = new ArrayList<>(stringList.size()); + stringList.stream().map(GuiUtils::stringSplit).forEach(newList::addAll); + return newList; + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/utils/HeadLib.java b/src/main/java/world/bentobox/challenges/utils/HeadLib.java new file mode 100644 index 0000000..3492d0a --- /dev/null +++ b/src/main/java/world/bentobox/challenges/utils/HeadLib.java @@ -0,0 +1,298 @@ +/* + * Written in 2018 by Daniel Saukel + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software + * to the public domain worldwide. + * + * This software is distributed without any warranty. + * + * You should have received a copy of the CC0 Public Domain Dedication + * along with this software. If not, see . + * + * @url https://github.com/DRE2N/HeadLib + */ +package world.bentobox.challenges.utils; + + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import java.util.*; + +import net.minecraft.server.v1_13_R2.NBTBase; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NBTTagList; + + +/** + * @author Daniel Saukel + * + * BONNe modified it for BentoBox by leaving only mob heads and removing unused code. + */ +public enum HeadLib +{ +// --------------------------------------------------------------------- +// Section: Library of All Mob heads +// --------------------------------------------------------------------- + + /** + * All enum values. + */ + SPIDER("8bdb71d0-4724-48b2-9344-e79480424798", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2Q1NDE1NDFkYWFmZjUwODk2Y2QyNThiZGJkZDRjZjgwYzNiYTgxNjczNTcyNjA3OGJmZTM5MzkyN2U1N2YxIn19fQ=="), + CAVE_SPIDER("39173a7a-c957-4ec1-ac1a-43e5a64983df", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDE2NDVkZmQ3N2QwOTkyMzEwN2IzNDk2ZTk0ZWViNWMzMDMyOWY5N2VmYzk2ZWQ3NmUyMjZlOTgyMjQifX19"), + ENDERMAN("0de98464-1274-4dd6-bba8-370efa5d41a8", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2E1OWJiMGE3YTMyOTY1YjNkOTBkOGVhZmE4OTlkMTgzNWY0MjQ1MDllYWRkNGU2YjcwOWFkYTUwYjljZiJ9fX0="), + SLIME("7f0b0873-df6a-4a19-9bcd-f6c90ef804c7", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODk1YWVlYzZiODQyYWRhODY2OWY4NDZkNjViYzQ5NzYyNTk3ODI0YWI5NDRmMjJmNDViZjNiYmI5NDFhYmU2YyJ9fX0="), + GUARDIAN("f3898fe0-04fb-4f9c-8f8b-146a1d894007", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzI1YWY5NjZhMzI2ZjlkOTg0NjZhN2JmODU4MmNhNGRhNjQ1M2RlMjcxYjNiYzllNTlmNTdhOTliNjM1MTFjNiJ9fX0="), + GHAST("807f287f-6499-4e93-a887-0a298ab3091f", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGI2YTcyMTM4ZDY5ZmJiZDJmZWEzZmEyNTFjYWJkODcxNTJlNGYxYzk3ZTVmOTg2YmY2ODU1NzFkYjNjYzAifX19"), + BLAZE("7ceb88b2-7f5f-4399-abb9-7068251baa9d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjc4ZWYyZTRjZjJjNDFhMmQxNGJmZGU5Y2FmZjEwMjE5ZjViMWJmNWIzNWE0OWViNTFjNjQ2Nzg4MmNiNWYwIn19fQ=="), + MAGMA_CUBE("96aced64-5b85-4b99-b825-53cd7a9f9726", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzg5NTdkNTAyM2M5MzdjNGM0MWFhMjQxMmQ0MzQxMGJkYTIzY2Y3OWE5ZjZhYjM2Yjc2ZmVmMmQ3YzQyOSJ9fX0="), + WITHER("119c371b-ea16-47c9-ad7f-23b3d894520a", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2RmNzRlMzIzZWQ0MTQzNjk2NWY1YzU3ZGRmMjgxNWQ1MzMyZmU5OTllNjhmYmI5ZDZjZjVjOGJkNDEzOWYifX19"), + ENDER_DRAGON("433562fa-9e23-443e-93b0-d67228435e77", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRlY2MwNDA3ODVlNTQ2NjNlODU1ZWYwNDg2ZGE3MjE1NGQ2OWJiNGI3NDI0YjczODFjY2Y5NWIwOTVhIn19fQ=="), + SHULKER("d700b0b9-be74-4630-8cb5-62c979828ef6", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjFkMzUzNGQyMWZlODQ5OTI2MmRlODdhZmZiZWFjNGQyNWZmZGUzNWM4YmRjYTA2OWU2MWUxNzg3ZmYyZiJ9fX0="), + CREEPER("eed2d903-ca32-4cc7-b33b-ca3bdbe18da4", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjQyNTQ4MzhjMzNlYTIyN2ZmY2EyMjNkZGRhYWJmZTBiMDIxNWY3MGRhNjQ5ZTk0NDQ3N2Y0NDM3MGNhNjk1MiJ9fX0="), + ZOMBIE("9959dd98-efb3-4ee9-a8fb-2fda0218cda0", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTZmYzg1NGJiODRjZjRiNzY5NzI5Nzk3M2UwMmI3OWJjMTA2OTg0NjBiNTFhNjM5YzYwZTVlNDE3NzM0ZTExIn19fQ=="), + ZOMBIE_VILLAGER("bcaf2b85-d421-47cc-a40a-455e77bfb60b", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzdlODM4Y2NjMjY3NzZhMjE3YzY3ODM4NmY2YTY1NzkxZmU4Y2RhYjhjZTljYTRhYzZiMjgzOTdhNGQ4MWMyMiJ9fX0="), + ZOMBIE_PIGMAN("6540c046-d6ea-4aff-9766-32a54ebe6958", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRlOWM2ZTk4NTgyZmZkOGZmOGZlYjMzMjJjZDE4NDljNDNmYjE2YjE1OGFiYjExY2E3YjQyZWRhNzc0M2ViIn19fQ=="), + DOG("9655594c-5b1c-48a5-8e12-ffd7e0c735f2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDk1MTgzY2E0Y2RkMjk2MjhmZTZjNzIyZjc3OTA4N2I4M2MyMWJhOTdmNDIyNWU0YWQ5YjNlNjE4ZWNjZDMwIn19fQ=="), + HORSE("c6abc94e-a5ff-45fe-a0d7-4e479f290a6f", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDJlYjk2N2FiOTRmZGQ0MWE2MzI1ZjEyNzdkNmRjMDE5MjI2ZTVjZjM0OTc3ZWVlNjk1OTdmYWZjZjVlIn19fQ=="), + TURTLE("ef56c7a3-a5e7-4a7f-9786-a4b6273a591d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTJlNTQ4NDA4YWI3NWQ3ZGY4ZTZkNWQyNDQ2ZDkwYjZlYzYyYWE0ZjdmZWI3OTMwZDFlZTcxZWVmZGRmNjE4OSJ9fX0="), + OCELOT("664dd492-3fcd-443b-9e61-4c7ebd9e4e10", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTY1N2NkNWMyOTg5ZmY5NzU3MGZlYzRkZGNkYzY5MjZhNjhhMzM5MzI1MGMxYmUxZjBiMTE0YTFkYjEifX19"), + SHEEP("fa234925-9dbe-4b8f-a544-7c70fb6b6ac5", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjMxZjljY2M2YjNlMzJlY2YxM2I4YTExYWMyOWNkMzNkMThjOTVmYzczZGI4YTY2YzVkNjU3Y2NiOGJlNzAifX19"), + COW("97ddf3b3-9dbe-4a3b-8a0f-1b19ddeac0bd", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWQ2YzZlZGE5NDJmN2Y1ZjcxYzMxNjFjNzMwNmY0YWVkMzA3ZDgyODk1ZjlkMmIwN2FiNDUyNTcxOGVkYzUifX19"), + CHICKEN("7d3a8ace-e045-4eba-ab71-71dbf525daf1", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTYzODQ2OWE1OTljZWVmNzIwNzUzNzYwMzI0OGE5YWIxMWZmNTkxZmQzNzhiZWE0NzM1YjM0NmE3ZmFlODkzIn19fQ=="), + PIG("e1e1c2e4-1ed2-473d-bde2-3ec718535399", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjIxNjY4ZWY3Y2I3OWRkOWMyMmNlM2QxZjNmNGNiNmUyNTU5ODkzYjZkZjRhNDY5NTE0ZTY2N2MxNmFhNCJ9fX0="), + SQUID("f95d9504-ea2b-4b89-b2d0-d400654a7010", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMDE0MzNiZTI0MjM2NmFmMTI2ZGE0MzRiODczNWRmMWViNWIzY2IyY2VkZTM5MTQ1OTc0ZTljNDgzNjA3YmFjIn19fQ=="), + MUSHROOM_COW("e206ac29-ae69-475b-909a-fb523d894336", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDBiYzYxYjk3NTdhN2I4M2UwM2NkMjUwN2EyMTU3OTEzYzJjZjAxNmU3YzA5NmE0ZDZjZjFmZTFiOGRiIn19fQ=="), + ELDER_GUARDIAN("f2e933a7-614f-44e0-bf18-289b102104ab", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWM3OTc0ODJhMTRiZmNiODc3MjU3Y2IyY2ZmMWI2ZTZhOGI4NDEzMzM2ZmZiNGMyOWE2MTM5Mjc4YjQzNmIifX19"), + STRAY("644c9bad-958b-43ce-9d2f-199d85be607c", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzhkZGY3NmU1NTVkZDVjNGFhOGEwYTVmYzU4NDUyMGNkNjNkNDg5YzI1M2RlOTY5ZjdmMjJmODVhOWEyZDU2In19fQ=="), + HUSK("2e387bc6-774b-4fda-ba22-eb54a26dfd9e", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzc3MDY4MWQxYTI1NWZiNGY3NTQ3OTNhYTA1NWIyMjA0NDFjZGFiOWUxMTQxZGZhNTIzN2I0OTkzMWQ5YjkxYyJ9fX0="), + SKELETON_HORSE("bcbce5bf-86c4-4e62-9fc5-0cc90de94b6d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdlZmZjZTM1MTMyYzg2ZmY3MmJjYWU3N2RmYmIxZDIyNTg3ZTk0ZGYzY2JjMjU3MGVkMTdjZjg5NzNhIn19fQ=="), + ZOMBIE_HORSE("506ced1a-dac8-4d84-b341-645fbb297335", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2Q2YjllZjhkZDEwYmE2NDE0MjJiNDQ5ZWQxNWFkYzI5MmQ3M2Y1NzI5ODRkNDdlMjhhMjI2YWE2ZWRkODcifX19"), + DONKEY("3da7917b-cb95-40b3-a516-9befa4f4d71d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjEyNTJjMjI1MGM0NjhkOWZkZTUzODY3Nzg1NWJjOWYyODQzM2RmNjkyNDdkNzEzODY4NzgxYjgyZDE0YjU1In19fQ=="), + MULE("fac6815e-02d5-4776-a5d6-f6d6535b7831", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzY5Y2E0YzI5NTZhNTY3Yzk2ZWUwNGM1MzE0OWYxODY0NjIxODM5M2JjN2IyMWVkNDVmZGFhMTNiZWJjZGFkIn19fQ=="), + EVOKER("36ee7e5b-c092-48ad-9673-2a73b0a44b4f", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTAwZDNmZmYxNmMyZGNhNTliOWM1OGYwOTY1MjVjODY5NzExNjZkYmFlMTMzYjFiMDUwZTVlZTcxNjQ0MyJ9fX0="), + VEX("f83bcfc1-0213-4957-888e-d3e2fae71203", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWU3MzMwYzdkNWNkOGEwYTU1YWI5ZTk1MzIxNTM1YWM3YWUzMGZlODM3YzM3ZWE5ZTUzYmVhN2JhMmRlODZiIn19fQ=="), + VINDICATOR("5f958e1c-91ea-42d3-9d26-09e5925f2d9c", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2RhNTg1ZWJkZGNjNDhmMzA3YmU2YTgzOTE2Zjg3OGVkNGEwMTRlYzNkNGYyODZhMmNmZDk1MzI4MTk2OSJ9fX0="), + ILLUSIONER("ccb79aa9-1764-4e5b-8ff3-e7be661ac7e2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjYxNWUxMjQ1ZDBkODJkODFkZmEzNzUzMDYzZDhhYWQwZmE2NjU3NTk5ODcxY2Y0YzY5YmFiNzNjNjk5MDU1In19fQ=="), + PIG_ZOMBIE("6540c046-d6ea-4aff-9766-32a54ebe6958", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzRlOWM2ZTk4NTgyZmZkOGZmOGZlYjMzMjJjZDE4NDljNDNmYjE2YjE1OGFiYjExY2E3YjQyZWRhNzc0M2ViIn19fQ=="), + SILVERFISH("30a4cd5c-5754-4db8-8960-18022a74627d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGE5MWRhYjgzOTFhZjVmZGE1NGFjZDJjMGIxOGZiZDgxOWI4NjVlMWE4ZjFkNjIzODEzZmE3NjFlOTI0NTQwIn19fQ=="), + BAT("cfdaf903-18cf-4a92-acf2-efa8626cf0b2", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWU5OWRlZWY5MTlkYjY2YWMyYmQyOGQ2MzAyNzU2Y2NkNTdjN2Y4YjEyYjlkY2E4ZjQxYzNlMGEwNGFjMWNjIn19fQ=="), + WITCH("7f92b3d6-5ee0-4ab6-afae-2206b9514a63", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjBlMTNkMTg0NzRmYzk0ZWQ1NWFlYjcwNjk1NjZlNDY4N2Q3NzNkYWMxNmY0YzNmODcyMmZjOTViZjlmMmRmYSJ9fX0="), + ENDERMITE("33c425bb-a294-4e01-9b5b-a8ad652bb5cf", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODRhYWZmYTRjMDllMmVhZmI4NWQzNTIyMTIyZGIwYWE0NTg3NGJlYTRlM2Y1ZTc1NjZiNGQxNjZjN2RmOCJ9fX0="), + WOLF("4aabc2be-340a-46ad-a42b-0c348344750a", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdhZGU0OWY1MDEzMTExNTExZGM1MWJhYjc2OWMxYWQ2OTUzMTlhNWQzNTViMzZhZTkyMzRlYTlkMWZmOGUifX19"), + SNOWMAN("d71e165b-b49d-4180-9ccf-8ad3084df1dc", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTdlOTgzMWRhZjY4MWY4YzRjNDc3NWNiNDY1M2MzNGJlMjg5OGY4N2VmZDNiNTk4ZDU1NTUxOGYyZmFjNiJ9fX0="), + IRON_GOLEM("7cb6e9a5-994f-40d5-9bfc-4ba5d796d21e", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODkwOTFkNzllYTBmNTllZjdlZjk0ZDdiYmE2ZTVmMTdmMmY3ZDQ1NzJjNDRmOTBmNzZjNDgxOWE3MTQifX19"), + RABBIT("2186bdc6-55b1-4b44-8a46-3c8a11d40f3d", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2QxMTY5YjI2OTRhNmFiYTgyNjM2MDk5MjM2NWJjZGE1YTEwYzg5YTNhYTJiNDhjNDM4NTMxZGQ4Njg1YzNhNyJ9fX0="), + POLAR_BEAR("87324464-1700-468f-8333-e7779ec8c21e", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDQ2ZDIzZjA0ODQ2MzY5ZmEyYTM3MDJjMTBmNzU5MTAxYWY3YmZlODQxOTk2NjQyOTUzM2NkODFhMTFkMmIifX19"), + LLAMA("75fb08e5-2419-46fa-bf09-57362138f234", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzJiMWVjZmY3N2ZmZTNiNTAzYzMwYTU0OGViMjNhMWEwOGZhMjZmZDY3Y2RmZjM4OTg1NWQ3NDkyMTM2OCJ9fX0="), + PARROT("da0cac14-3763-45df-b884-c99a567882ac", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTZkZTFlYjllMzI1ZTYyZjI4ZjJjMTgzZDM5YTY4MzExMzY0NDYzNjU3MjY0Njc1YThiNDYxY2QyOGM5In19fQ=="), + VILLAGER("b3ed4a1b-dfff-464c-87c0-c8029e1de47b", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzZhYjYxYWNlMTM2MDE3YTg3YjFiODFiMTQ1ZWJjNjNlMmU2ZGE5ZDM2NGM4MTE5NGIzM2VlODY2ZmU0ZCJ9fX0="), + PHANTOM("9290add8-c291-4a5a-8f8a-594f165406a3", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2U5NTE1M2VjMjMyODRiMjgzZjAwZDE5ZDI5NzU2ZjI0NDMxM2EwNjFiNzBhYzAzYjk3ZDIzNmVlNTdiZDk4MiJ9fX0="), + COD("d6d4c744-06b4-4a8a-bc7a-bdb0770bb1cf", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MmQ3ZGQ2YWFkZjM1Zjg2ZGEyN2ZiNjNkYTRlZGRhMjExZGY5NmQyODI5ZjY5MTQ2MmE0ZmIxY2FiMCJ9fX0="), + SALMON("0354c430-3979-4b6e-8e65-a99eb3ea8818", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGFlYjIxYTI1ZTQ2ODA2Y2U4NTM3ZmJkNjY2ODI4MWNmMTc2Y2VhZmU5NWFmOTBlOTRhNWZkODQ5MjQ4NzgifX19"), + PUFFERFISH("258e3114-368c-48a1-85fd-be580912f0df", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTcxNTI4NzZiYzNhOTZkZDJhMjI5OTI0NWVkYjNiZWVmNjQ3YzhhNTZhYzg4NTNhNjg3YzNlN2I1ZDhiYiJ9fX0="), + TROPICAL_FISH("d93c1bf6-616f-401a-af6e-f9b9803a0024", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTc5ZTQ4ZDgxNGFhM2JjOTg0ZThhNmZkNGZiMTcwYmEwYmI0ODkzZjRiYmViZGU1ZmRmM2Y4Zjg3MWNiMjkyZiJ9fX0="), + DROWNED("2f169660-61be-46bd-acb5-1abef9fe5731", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzNmN2NjZjYxZGJjM2Y5ZmU5YTYzMzNjZGUwYzBlMTQzOTllYjJlZWE3MWQzNGNmMjIzYjNhY2UyMjA1MSJ9fX0="), + DOLPHIN("8b7ccd6d-36de-47e0-8d5a-6f6799c6feb8", "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU5Njg4Yjk1MGQ4ODBiNTViN2FhMmNmY2Q3NmU1YTBmYTk0YWFjNmQxNmY3OGU4MzNmNzQ0M2VhMjlmZWQzIn19fQ=="); + +// --------------------------------------------------------------------- +// Section: Variables +// --------------------------------------------------------------------- + + /** + * User UUID that has given skin. + */ + private String uuid; + + /** + * Base64 Encoded link to Minecraft texture. + */ + private String textureValue; + + /** + * Skull owner. + */ + private Object skullOwner; + + /** + * This map allows to access all enum values via their string. + */ + private final static Map BY_NAME = new HashMap<>(); + + +// --------------------------------------------------------------------- +// Section: Constructor +// --------------------------------------------------------------------- + + + /** + * This inits new enum value by given UUID and textureValue that is encoded in Base64. + * @param uuid User UUID String which skin must be loaded. + * @param textureValue Texture link encoded in Base64. + */ + HeadLib(String uuid, String textureValue) + { + this.uuid = uuid; + this.textureValue = textureValue; + } + + +// --------------------------------------------------------------------- +// Section: Methods that returns ItemStacks +// --------------------------------------------------------------------- + + /** + * Returns an ItemStack of the size 1 of the custom head. + * + * @return an ItemStack of the custom head. + */ + public ItemStack toItemStack() + { + return this.toItemStack(1); + } + + + /** + * Returns an ItemStack of the custom head. + * + * @param amount the amount of items in the stack + * @return an ItemStack of the custom head. + */ + public ItemStack toItemStack(int amount) + { + return this.toItemStack(amount, null); + } + + + /** + * Returns an ItemStack of the size 1 of the custom head. + * + * @param displayName the name to display. Supports "&" color codes + * @param loreLines optional lore lines. Supports "&" color codes + * @return an ItemStack of the custom head. + */ + public ItemStack toItemStack(String displayName, String... loreLines) + { + return this.toItemStack(1, displayName, loreLines); + } + + + /** + * Returns an ItemStack of the custom head. + * + * @param amount the amount of items in the stack + * @param displayName the name to display. Supports "&" color codes + * @param loreLines optional lore lines. Supports "&" color codes + * @return an ItemStack of the custom head. + */ + public ItemStack toItemStack(int amount, String displayName, String... loreLines) + { + ItemStack item = new ItemStack(Material.PLAYER_HEAD, amount); + + if (displayName != null) + { + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(ChatColor.translateAlternateColorCodes('&', displayName)); + + if (loreLines.length != 0) + { + List loreCC = new ArrayList<>(); + Arrays.stream(loreLines).forEach(l -> loreCC.add(ChatColor.translateAlternateColorCodes('&', l))); + meta.setLore(loreCC); + } + + item.setItemMeta(meta); + } + + return this.setSkullOwner(item, this.getSkullOwner()); + } + + +// --------------------------------------------------------------------- +// Section: Private inner methods +// --------------------------------------------------------------------- + + + /** + * This method returns SkullOwner or create new one if it is not created yet. + * SkullOwner is NBTTagCompound object that contains information about player_head skin. + * @return skullOwner object. + */ + private Object getSkullOwner() + { + if (this.skullOwner == null) + { + this.skullOwner = this.createOwnerCompound(this.uuid, this.textureValue); + } + + return this.skullOwner; + } + + + /** + * This method creates new NBTTagCompound object that contains UUID and texture link. + * @param id - UUID of user. + * @param textureValue - Encoded texture string. + * @return NBTTagCompound object that contains all necessary information about player_head skin. + */ + private NBTTagCompound createOwnerCompound(String id, String textureValue) + { + NBTTagCompound skullOwner = new NBTTagCompound(); + skullOwner.setString("Id", id); + NBTTagCompound properties = new NBTTagCompound(); + NBTTagList textures = new NBTTagList(); + NBTTagCompound value = new NBTTagCompound(); + value.setString("Value", textureValue); + textures.add(value); + properties.set("textures", textures); + skullOwner.set("Properties", properties); + + return skullOwner; + } + + + /** + * This method adds SkullOwner tag to given item. + * @param item Item whom SkullOwner tag must be added. + * @param compound NBTTagCompound object that contains UUID and texture link. + * @return new copy of given item with SkullOwner tag. + */ + private ItemStack setSkullOwner(ItemStack item, Object compound) + { + net.minecraft.server.v1_13_R2.ItemStack nmsStack = CraftItemStack.asNMSCopy(item); + nmsStack.getOrCreateTag().set("SkullOwner", (NBTBase) compound); + return CraftItemStack.asBukkitCopy(nmsStack); + } + + +// --------------------------------------------------------------------- +// Section: Other methods +// --------------------------------------------------------------------- + + + /** + * This method returns HeadLib enum object with given name. If enum value with given name does not exist, + * then return null. + * @param name Name of object that must be returned. + * @return HeadLib with given name or null. + */ + public static HeadLib getHead(String name) + { + return BY_NAME.get(name.toUpperCase()); + } + + + // + // This static call populates all existing enum values into static map. + // + static + { + for (HeadLib head : values()) + { + BY_NAME.put(head.name(), head); + } + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/challenges/LevelStatus.java b/src/main/java/world/bentobox/challenges/utils/LevelStatus.java similarity index 74% rename from src/main/java/world/bentobox/challenges/LevelStatus.java rename to src/main/java/world/bentobox/challenges/utils/LevelStatus.java index 3fb0c90..5e99cf2 100644 --- a/src/main/java/world/bentobox/challenges/LevelStatus.java +++ b/src/main/java/world/bentobox/challenges/utils/LevelStatus.java @@ -1,6 +1,6 @@ -package world.bentobox.challenges; +package world.bentobox.challenges.utils; -import world.bentobox.challenges.database.object.ChallengeLevels; +import world.bentobox.challenges.database.object.ChallengeLevel; /** * Level status class @@ -8,8 +8,8 @@ import world.bentobox.challenges.database.object.ChallengeLevels; * */ public class LevelStatus { - private final ChallengeLevels level; - private final ChallengeLevels previousLevel; + private final ChallengeLevel level; + private final ChallengeLevel previousLevel; private final int numberOfChallengesStillToDo; private final boolean complete; private final boolean isUnlocked; @@ -21,7 +21,7 @@ public class LevelStatus { * @param complete - whether complete or not * @param isUnlocked */ - public LevelStatus(ChallengeLevels level, ChallengeLevels previousLevel, int numberOfChallengesStillToDo, boolean complete, boolean isUnlocked) { + public LevelStatus(ChallengeLevel level, ChallengeLevel previousLevel, int numberOfChallengesStillToDo, boolean complete, boolean isUnlocked) { super(); this.level = level; this.previousLevel = previousLevel; @@ -32,7 +32,7 @@ public class LevelStatus { /** * @return the level */ - public ChallengeLevels getLevel() { + public ChallengeLevel getLevel() { return level; } /** @@ -44,7 +44,7 @@ public class LevelStatus { /** * @return the previousLevel */ - public ChallengeLevels getPreviousLevel() { + public ChallengeLevel getPreviousLevel() { return previousLevel; } /** diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9760fcd..c1a030c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,16 +1,31 @@ -# Challenges Config - +# Challenges Configuration ${project.version} +# This config file is dynamic and saved when the server is shutdown. +# You cannot edit it while the server is running because changes will +# be lost! Use in-game settings GUI or edit when server is offline. +# +# # Reset Challenges - if this is true, player's challenges will reset when they # reset an island or if they are kicked or leave a team. Prevents exploiting the # challenges by doing them repeatedly. -resetchallenges: true - +resetChallenges: true +# # Broadcast 1st time challenge completion messages to all players. # Change to false if the spam becomes too much. -broadcastmessages: true - -# Remove non-repeatable challenges from the challenge GUI when complete -removecompleteonetimechallenges: false - +broadcastMessages: true +# +# Remove non-repeatable challenges from the challenge GUI when complete. +removeCompleteOneTimeChallenges: false +# # Add enchanted glow to completed challenges -addcompletedglow: true +addCompletedGlow: true +# +# This indicate if free challenges must be at the start (true) or at the end (false) of list. +freeChallengesFirst: false +# +# This list stores GameModes in which Challenges addon should not work. +# To disable addon it is necessary to write its name in new line that starts with -. Example: +# disabled-gamemodes: +# - BSkyBlock +disabled-gamemodes: [] +# +uniqueId: config diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index cfbd361..fff97b6 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -6,98 +6,213 @@ ### Credits ### # Tastybento: maintainer -challenges: - admin: - challenge-created: "[challenge] created!" - complete: - description: "Mark challenge complete" - parameters: " " - unknown-challenge: "&cUnknown challenge" - create: - description: "&6Collect:" - description-item-color: "&B" - inventory: - description: "create an inventory challenge" - parameters: "[challenge name]" - surrounding: - description: "create a surrounding challenge" - hit-things: "Hit things to add them to the list of things required. Right click when done." - parameters: "[challenge name]" - description: "challenges admin" - error: - no-name: "You must include a challenge name" - gui-title: "&aChallenges Admin" - import: - description: "import challenges from challenges.yml" - imported: "Imported '[challenge]'" - levels: "Importing levels: [levels]" - no-file: "&cCould not find challenges.yml file to import!" - no-levels: "Warning: No levels defined in challenges.yml" - no-load: "&cError: Could not load challenges.yml. [message]" - number: "Imported [number] challenges" - overwriting: "Overwriting '[challenge]'" - parameters: "[overwrite]" - skipping: "'[challenge]' already exists - skipping" - parameters: "" - reload: - description: "reload challenges from the database" - parameters: "" - reset: - description: "Reset challenge to 0 times / incomplete" - parameters: " " - seticon: - description: "sets the challenge icon to inhand item" - error: - no-such-challenge: "&cNo such challenge name" - parameters: "[challenge name]" - you-added: "You added one [thing] to the challenge" - challenge: - format: "[description]" - complete: "&BComplete" - completechallenge: - challange-completed: "Challenge: [challengename] has been completed for [name]" - completed-times: "Completed [donetimes] out of [maxtimes]" - description: "Open the challenges menu" - error: - could-not-save: "&cCould not save the challenge!" - island-level: "&cYour island must be level [number] to complete this challenge!" - items-not-there: "&cAll required items must be close to you on your island!" - no-items-clicked: "&cYou did not click on anything. Cancelling." - not-close-enough: "&cYou must be standing within [number] blocks of all required items." - not-enough-items: "&cYou do not have enough [items] to complete this challenge!" - not-on-island: "&cYou must be on your island to do that!" - reward-problem: "&cThere was a problem giving your reward. Ask Admin to check log!" - you-still-need: "&cYou still need [amount] x [item]" - exp-reward: "&6Exp reward: [reward]" - first-time-rewards: "&6First time reward(s)" - gui-title: "&aChallenges" - help: - command: "/challenges: &fshow challenges" - config-reloaded: "Configuration reloaded from file." - reset-all-challenges: "resets all of the player's challenges" - reset-challenge: "marks a challenge as incomplete" - reset-challenge-for-all: "globally resets a challenge for every player with an optional repetition" - incomplete: Incomplete - item-take-warning: "&cAll required items are|&ctaken when you complete|&cthis challenge!" - items-closeby: "&cAll required items|&cmust be close to you|&con your island!" - level: "&FLevel: [level]" - max-reached: "Max reached [donetimes] out of [maxtimes]" - money-reward: "&6Money reward: $[reward]" - name: "Challenge Name" - name-has-completed: "[name] has completed the [challenge] challenge!" - navigation: "Click to see [level] challenges!" - not-repeatable: "This challenge is not repeatable!" - parameters: "[Level]" - repeat-rewards: "&6Repeat reward(s)" - repeatable: "This challenge can be repeated [maxtimes] times" - resetallchallenges: - success: "[name] has had all challenges reset." - resetchallenge: - challenge-reset: "Challenge: [challengename] has been reset for [name]" - error-challenge-does-not-exist: "Challenge doesn't exist or isn't yet completed" - rewards: "&FReward(s)" - to-complete: "Complete [challengesToDo] more [thisLevel] challenges to unlock this level!" - you-completed: "You completed the [challenge] challenge!" - you-repeated: "You repeated the [challenge] challenge!" - not-enough-money: "It is necessary to have [money] on your account to complete the challenge." - not-enough-exp: "It is necessary to have [xp] EXP to complete challenge." \ No newline at end of file +challenges: + admin: + challenge-created: '[challenge] created!' + complete: + description: Mark challenge complete + parameters: + unknown-challenge: '&cUnknown challenge' + create: + description: '&6Collect:' + description-item-color: '&B' + inventory: + description: create an inventory challenge + parameters: '[challenge name]' + surrounding: + description: create a surrounding challenge + hit-things: Hit things to add them to the list of things required. Right click when done. + parameters: '[challenge name]' + description: challenges admin + error: + no-name: You must include a challenge name + gui-title: '&aChallenges Admin' + import: + add: 'Adding new challenge: [object]' + description: import challenges from challenges.yml + imported: Imported '[challenge]' + levels: 'Importing levels: [levels]' + no-file: '&cCould not find challenges.yml file to import!' + no-levels: 'Warning: No levels defined in challenges.yml' + no-load: '&cError: Could not load challenges.yml. [message]' + number: Imported [number] challenges + overwriting: Overwriting '[challenge]' + parameters: '[overwrite]' + skipping: '''[challenge]'' already exists - skipping' + parameters: '' + reload: + description: reload challenges from the database + parameters: '' + reset: + description: Reset challenge to 0 times / incomplete + parameters: + seticon: + description: sets the challenge icon to inhand item + error: + no-such-challenge: '&cNo such challenge name' + parameters: '[challenge name]' + you-added: You added one [thing] to the challenge + blocks-take-warning: Blocks will be removed. + challenge: + format: '[description]' + complete: '&BComplete' + completechallenge: + challange-completed: 'Challenge: [challengename] has been completed for [name]' + completed-times: Completed [donetimes] out of [maxtimes] + description: Open the challenges menu + entities-kill-warning: Entities will be killed. + error: + could-not-save: '&cCould not save the challenge!' + island-level: '&cYour island must be level [number] to complete this challenge!' + items-not-there: '&cAll required items must be close to you on your island!' + no-items-clicked: '&cYou did not click on anything. Cancelling.' + not-close-enough: '&cYou must be standing within [number] blocks of all required + items.' + not-enough-items: '&cYou do not have enough [items] to complete this challenge!' + not-on-island: '&cYou must be on your island to do that!' + reward-problem: '&cThere was a problem giving your reward. Ask Admin to check + log!' + you-still-need: '&cYou still need [amount] x [item]' + not-deployed: '&cChallenge is not deployed.' + errors: + challenge-level-not-available: You have not unlocked level to complete this challenge. + unique-id: Unique ID [id] is already taken. Choose different. + wrong-environment: You are in wrong environment! + wrong-icon: Material [icon] is not recognized + exp-reward: '&6Exp reward: [reward]' + first-time-rewards: '&6First time reward(s)' + gui: + admin: + buttons: + add-challenge: Create Challenge + add-level: Create Level + blocks: Blocks + broadcast: Broadcast Messages + challenges: Challenges + complete: Complete + delete-challenge: Delete Challenge + delete-level: Delete Level + deployed: Deployment status + description: Description + edit-challenge: Edit Challenge + edit-level: Edit Level + entities: Entities + environment: Environment + free-challenges: Free Challenges Position + glow: Completion Glowing + icon: Icon + name: Name + order: Order Number + permissions: Permissions + properties: Properties + remove-blocks: Remove Blocks + remove-challenge: Remove Challenge + remove-entities: Remove Entities + remove-exp: Take Experience + remove-items: Remove items + remove-money: Remove Money + remove-on-complete: Remove on completion + repeat-count: Repeat Count + repeat-reward-command: Repeat Reward Command + repeat-reward-exp: Repeat Reward Experience + repeat-reward-items: Repeat Reward Items + repeat-reward-money: Repeat Reward Money + repeat-reward-text: Repeat Reward Text + repeatable: Repeatable + required-exp: Required Experience + required-items: Required items + required-level: Required Island Level + required-money: Required Money + requirements: Requirements + reset: Reset + reward-command: Reward Command + reward-exp: Reward Experience + reward-items: Reward Items + reward-money: Reward Money + reward-text: Reward Text + rewards: Rewards + search-radius: Search radius + settings: Settings + toggle-users: Choose players + type: 'Challenge Type: [value]' + waiver-amount: Waiver Amount + import: Import + choose-challenge-title: Challenges List + choose-level-title: Levels List + choose-user-title: Users List + descriptions: + broadcast: Broadcast 1st time challenge completion messages to all players. + disabled: disabled + enabled: enabled + free-challenges: This indicate if free challenges must be at the start (true) or at the end (false) of list. + glow: Add enchanted glow to completed challenges + in_world: In World + inventory: This type of challenges allows to define blocks and entities on island requirements. + island: This type of challenges allows to define inventory item requirements. + nether: Nether + normal: Normal + online: Online + order: Current order [value] + other: This type of challenges allows to define Money, Experience or island level requirements. + remove-on-complete: Remove non-repeatable challenges from the challenge GUI when complete. + repeat-count: 'Current value: [value]' + repeat-reward-exp: 'Current value: [value]' + repeat-reward-money: 'Current value: [value]' + required-exp: Current necessary [value] + required-level: 'Current value: [value]' + required-money: 'Current value: [value]' + reset: Reset Challenges - if this is true, player's challenges will reset when they reset an island or if they are kicked or leave a team + reward-exp: 'Current value: [value]' + reward-money: 'Current value: [value]' + search-radius: Current radius [value] + the_end: The End + waiver-amount: 'Current value: [value]' + with_island: With Island + edit-challenge-title: Edit Challenge + edit-entities: Manage Entities + edit-level-title: Edit Levels + manage-blocks: Manage Blocks + settings-title: Edit Settings + button: + add: Add + remove-selected: Remove Selected + buttons: + back: Return + next: Next + previous: Previous + title: '"Challenges GUI"' + gui-title: '&aChallenges' + help: + command: '/challenges: &fshow challenges' + config-reloaded: Configuration reloaded from file. + reset-all-challenges: resets all of the player's challenges + reset-challenge: marks a challenge as incomplete + reset-challenge-for-all: globally resets a challenge for every player with an optional repetition + incomplete: Incomplete + item-take-warning: '&cAll required items are|&ctaken when you complete|&cthis + challenge!' + items-closeby: '&cAll required items|&cmust be close to you|&con your island!' + level: '&FLevel: [level]' + max-reached: Max reached [donetimes] out of [maxtimes] + maxed-reached: Completed [donetimes] out of [maxtimes] + money-reward: '&6Money reward: $[reward]' + name: Challenge Name + name-has-completed: '[name] has completed the [challenge] challenge!' + name-has-completed-level: '[name] completed all challenges in [level]!' + navigation: Click to see [level] challenges! + not-enough-exp: It is necessary to have [xp] EXP to complete challenge. + not-enough-money: It is necessary to have [money] on your account to complete the challenge. + not-repeatable: This challenge is not repeatable! + parameters: '[Level]' + repeat-rewards: '&6Repeat reward(s)' + repeatable: This challenge can be repeated [maxtimes] times + resetallchallenges: + success: '[name] has had all challenges reset.' + resetchallenge: + challenge-reset: 'Challenge: [challengename] has been reset for [name]' + error-challenge-does-not-exist: Challenge doesn't exist or isn't yet completed + rewards: '&FReward(s)' + to-complete: Complete [challengesToDo] more [thisLevel] challenges to unlock this level! + you-completed: You completed the [challenge] challenge! + you-completed-level: Congratulations, you complete [level]! + you-repeated: You repeated the [challenge] challenge! diff --git a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java index df581e7..b0d8992 100644 --- a/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java +++ b/src/test/java/world/bentobox/challenges/ChallengesAddonTest.java @@ -35,8 +35,8 @@ import org.powermock.modules.junit4.PowerMockRunner; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import world.bentobox.challenges.database.object.Challenges; -import world.bentobox.challenges.database.object.Challenges.ChallengeType; +import world.bentobox.challenges.database.object.Challenge; +import world.bentobox.challenges.database.object.Challenge.ChallengeType; /** * @author tastybento @@ -81,7 +81,7 @@ public class ChallengesAddonTest { public void test() { Gson gson = new GsonBuilder().setPrettyPrinting().create(); - Challenges challenges = new Challenges(); + Challenge challenges = new Challenge(); challenges.setChallengeType(ChallengeType.ISLAND); Map map = new HashMap<>(); map.put(Material.DIRT, 5); diff --git a/src/test/java/world/bentobox/challenges/ParseItemTest.java b/src/test/java/world/bentobox/challenges/ParseItemTest.java index f5eb767..7a46f8a 100644 --- a/src/test/java/world/bentobox/challenges/ParseItemTest.java +++ b/src/test/java/world/bentobox/challenges/ParseItemTest.java @@ -26,6 +26,7 @@ import world.bentobox.challenges.ChallengesAddon; import world.bentobox.challenges.ParseItem; @RunWith(PowerMockRunner.class) +@Deprecated public class ParseItemTest { private static ChallengesAddon addon;